keyup 事件处理程序更改焦点不适用于快速键入

keyup event handler changes focus not working for quick typing

我想要一个 PIN 输入表单,其中每个字符都输入到单独的 html 输入中。

我正在尝试捕获 javascript 中的 keyup 事件,以便我可以将焦点转移到下一个输入元素,从而使用户无需通过单击鼠标或 Tab 键自行更改它。

除非用户输入的速度非常快,否则它可以正常工作。例如,如果用户非常快地键入字符“1”和“2”,我发现第一个输入现在正确地包含字符“1”,而第二个输入仍然是空的,并且焦点已移至第三个输入。

为什么?

代码如下:

$(document).ready(function () {

            $('.pinchar').keyup(function (e) {
    
                if (
                    (e.which == 8) //backspace
                    || (e.which == 46) // del
                    || (e.which == 9) // tab
                    || (e.which == 13) // return
                    || (e.which == 27) // esc
                    || (e.which == 37) // arrow
                    || (e.which == 38) // arrow
                    || (e.which == 39) // arrow
                    || (e.which == 40) // arrow
                    || (e.which == 27) // esc
                    || (e.which == 20) // CAPS LOCK 
                    || (e.which == 17) // Ctrl
                    || (e.which == 18) // Alt 
                    || (e.which == 16)) { //shift

                    return false;
                } else {
                    $(this).parent().next().find('.pinchar').focus();
                }
            });
  
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="enterPINTable">
      <tr>
        <td>
          <input name="txtPIN1" id="txtPIN1" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN2" id="txtPIN2" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN3" id="txtPIN3" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN4" id="txtPIN4" class="pinchar" size="1">
        </td>
      </tr>
    </table>

keyup 按下一个键会失败,然后在松开之前再按另一个键。事件的顺序是 keydown 1keydown 2keyup 1keydown 2。但是,由于字符是在 keydown(和 keypress)之后生成的,在这种情况下,在第一个 keyup 事件之前,input 中已经有两个字符。

改为尝试 input 事件:

  $('.pinchar').on('input', function (e) {
       // ... etc.

任何变化都会立即触发。

但要正确处理其他键,您还需要捕获 keydown 事件(对于箭头键和退格键)。

我会建议这个代码:

$(document).ready(function () {
    var $inputs = $(".enterPINTable .pinchar");
    function step(inp, dir, clr) {
        if (clr) $(inp).val("");
        var buffer = $(inp).val(); // get excess characters
        // spread characters in next boxes to ensure max 1 char
        var curr = $inputs.index(inp);
        for (var i = curr, j = 0; j < buffer.length && i < $inputs.length; i++, j++) { 
             $inputs.eq(i).val(buffer[j]);
        }
        if (dir < 0) curr-= curr > 0;
        else curr = Math.min(curr + (j||dir), $inputs.length-1);
        var $next = $inputs.eq(curr);
        $next.focus();
        setTimeout(function () { 
            $next.select();
        }, 0);
        return false;
    }
    $inputs.on("focus", function () {
        $(this).select(); // always select the "whole" (1 char) text
    }).on("mouseup", function () { // Attempt to unselect?
        setTimeout(function () { // Keep character selected....
            $(this).select();
        }.bind(this));
    }).on("keydown", function (e) {
        if (e.key == "ArrowLeft" || e.key == "ArrowUp") return step(this, -1);
        if (e.key == "ArrowRight" || e.key == "ArrowDown") return step(this, 1);
        if (e.key == "Backspace") return step(this, -1, 1);
    }).on("input", function (e) {
        step(this, 0);
    }).eq(0).focus();
});
.pinchar { width: 1em; text-align: center }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="enterPINTable">
  <tr>
    <td>
      <input name="txtPIN1" id="txtPIN1" class="pinchar">
    </td>
    <td>
      <input name="txtPIN2" id="txtPIN2" class="pinchar">
    </td>
    <td>
      <input name="txtPIN3" id="txtPIN3" class="pinchar">
    </td>
    <td>
      <input name="txtPIN4" id="txtPIN4" class="pinchar">
    </td>
  </tr>
</table>

上面的代码旨在始终 select 当前字符,这样如果您键入另一个字符,它就会替换当前字符。如果您成功删除 selection(不容易)并尝试添加第二个字符,它将在下一个框中结束。从剪贴板粘贴一系列字符也会导致每个输入框一个字符的传播。

leftrighttabshift+tabbackspacedelete 键作为可以预料。

使用 CSS 调整输入的大小,并使文本居中,似乎可以提供更好的结果。

Keypress 更适合这种情况。

问题是 keyup 触发晚了,即在 keydown 和 keypress 之后。

顺序是这样的:

当按下键时,立即触发 keydown 事件。在那种情况下,该值不会打印在屏幕上。触发该按键后,此时将打印该值。
在这两个事件之后,当您将手指从键上抬起时,将触发 keyup。

因此,当您快速按下两个键时,会快速连续调用它们的按键(当值打印在屏幕上时),因此值会打印在同一输入上,然后调用两个键的 keyup 将焦点转发到下一个输入的下一个(将它们留空,因为在按键期间已经打印了值)。

$(document).ready(function () {

            $('.pinchar').keypress(function (e) {
    
               $(this).parent().next().find('.pinchar').focus();
               
            });
  
  });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table class="enterPINTable">
      <tr>
        <td>
          <input name="txtPIN1" id="txtPIN1" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN2" id="txtPIN2" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN3" id="txtPIN3" class="pinchar" size="1">
        </td>
        <td>
          <input name="txtPIN4" id="txtPIN4" class="pinchar" size="1">
        </td>
      </tr>
    </table>