You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

密码输入框显示最后输入字符后自动掩码的功能优化:解决中间字符删除异常问题

Fixing the Password Input with Visible Last Character & Proper Middle-Character Deletion

I totally get the frustration here—your current setup works for typing new characters, but falls apart when deleting from anywhere except the end. The core issue is that your original code only uses the length difference between the visible and hidden inputs to update the real password, which completely ignores where the cursor is and which character is actually being deleted.

Let's rebuild this solution to handle all edge cases: typing, deleting from any position, cursor navigation, and keeping the real password in sync perfectly. Here's a robust implementation:

Complete Working Code

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Visible password input (shows masks + last character temporarily) -->
<input id="visible-password" type="text" value="" />
<!-- Hidden input to store the actual password (submit this to your server) -->
<input id="real-password" type="hidden" value="" />

CSS

#visible-password {
  /* Style to match standard password input aesthetics */
  font-family: monospace;
}

JavaScript

$(document).ready(function() {
  const SHOW_LAST_CHAR_DURATION = 1000; // 1 second delay before masking last character
  let hideLastCharTimer;
  let lastVisibleValue = '';

  const $visibleInput = $('#visible-password');
  const $realInput = $('#real-password');

  // Helper: Get current cursor position in an input
  function getCursorPosition(input) {
    return input.selectionStart;
  }

  // Helper: Set cursor position in an input
  function setCursorPosition(input, position) {
    input.setSelectionRange(position, position);
  }

  // Update visible input to show masks + last character (if timer is active)
  function updateVisibleDisplay() {
    const realPassword = $realInput.val();
    if (realPassword.length === 0) {
      $visibleInput.val('');
      lastVisibleValue = '';
      return;
    }

    // Show last character only if timer is running; else show all masks
    const displayValue = hideLastCharTimer 
      ? realPassword.slice(0, -1).replace(/./g, '•') + realPassword.slice(-1)
      : realPassword.replace(/./g, '•');
    
    $visibleInput.val(displayValue);
    lastVisibleValue = displayValue;
  }

  // Handle typing/pasting events
  $visibleInput.on('input', function(e) {
    const visibleValue = $(this).val();
    const realPassword = $realInput.val();
    const cursorPos = getCursorPosition(this);

    // Find the range where the input changed
    let diffStart = 0;
    let diffEnd = Math.max(visibleValue.length, lastVisibleValue.length);
    
    while (diffStart < diffEnd && visibleValue[diffStart] === lastVisibleValue[diffStart]) {
      diffStart++;
    }
    while (diffEnd > diffStart && visibleValue[diffEnd - 1] === lastVisibleValue[diffEnd - 1]) {
      diffEnd--;
    }

    // Update real password with the new characters at the correct position
    const newRealPassword = realPassword.slice(0, diffStart) + visibleValue.slice(diffStart, diffEnd) + realPassword.slice(diffEnd);
    $realInput.val(newRealPassword);

    // Reset timer to hide last character after inactivity
    clearTimeout(hideLastCharTimer);
    hideLastCharTimer = setTimeout(() => {
      hideLastCharTimer = null;
      updateVisibleDisplay();
    }, SHOW_LAST_CHAR_DURATION);

    // Sync visible display and cursor position
    updateVisibleDisplay();
    setCursorPosition(this, cursorPos);
  });

  // Handle backspace/delete keys explicitly (input event doesn't capture full context)
  $visibleInput.on('keydown', function(e) {
    const key = e.key;
    if (key !== 'Backspace' && key !== 'Delete') return;

    const realPassword = $realInput.val();
    const cursorPos = getCursorPosition(this);

    let newRealPassword = realPassword;
    let newCursorPos = cursorPos;

    if (key === 'Backspace') {
      if (cursorPos === 0) return; // Nothing to delete
      newRealPassword = realPassword.slice(0, cursorPos - 1) + realPassword.slice(cursorPos);
      newCursorPos = cursorPos - 1;
    } else if (key === 'Delete') {
      if (cursorPos === realPassword.length) return; // Nothing to delete
      newRealPassword = realPassword.slice(0, cursorPos) + realPassword.slice(cursorPos + 1);
    }

    $realInput.val(newRealPassword);

    // Reset timer and update display
    clearTimeout(hideLastCharTimer);
    hideLastCharTimer = setTimeout(() => {
      hideLastCharTimer = null;
      updateVisibleDisplay();
    }, SHOW_LAST_CHAR_DURATION);

    updateVisibleDisplay();
    setCursorPosition(this, newCursorPos);
    e.preventDefault(); // Skip default input handling since we're managing it manually
  });

  // Keep cursor position valid when clicking into the input
  $visibleInput.on('click', function() {
    const cursorPos = getCursorPosition(this);
    setCursorPosition(this, Math.min(cursorPos, $realInput.val().length));
  });
});

Key Improvements Explained

  • Cursor Position Awareness: We use selectionStart and setSelectionRange to track exactly where the user is interacting, so we can update the real password at the correct position every time.
  • Separate Real/Visible State: The hidden input (#real-password) always stores the actual password, while the visible input only handles masked display logic—no more mixing up values.
  • Smart Input Diffing: For typing or pasting, we calculate exactly what changed between the previous and current visible input, ensuring the real password is updated accurately.
  • Explicit Delete Handling: We directly handle backspace and delete keys to fix the middle-character deletion issue, preventing default behavior so our custom logic takes over.
  • Cursor Sync: After updating the visible input, we reset the cursor to the correct position to avoid confusing jumps for the user.
  • Robust Timer Management: Every user interaction resets the timer, so the last character stays visible until 1 second after the last action.

This implementation handles all the edge cases your original code missed: deleting from the middle, cursor navigation, pasting text, and keeping the real password perfectly in sync with user actions.

内容的提问来源于stack exchange,提问作者Sara Ree

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 05:02:39