如何检测所按下的键是否会在<input>文本框中生成一个字符?

55

我有一个普通的文本框:

<input type="text"> 

我使用jQuery处理键盘事件:

$("input:text").keydown(function() {
    // keydown code
}).keypress(function() {
    // keypress code
}).keyup(function() {
    // keyup code
});
用户在文本框中按键盘上的不同按键(如字母、数字、SHIFT、BACKSPACE、SPACE等),我需要检测用户按下了哪个会增加文本框值的键。例如,按下"A"键将增加文本框的长度,而按下"SHIFT"键则不会。
我记得曾经看过PPK的一次讲座,在那里他提到了这两者之间的区别。这与事件-keydown与keypress-以及可能与事件属性-key、char、keyCode有关。
更新!
我需要在keydown或keypress处理程序中知道这些信息。我不能等待keyup事件发生。
为什么我需要这个:
我有一个文本框,它的大小根据用户的输入动态改变。您可以查看这个演示:http://vidasp.net/tinydemos/variable-size-text-box.html 在演示中,我有一个keydown和keyup处理程序。 keyup处理程序基于输入值调整文本框大小。但是,keydown处理程序将大小设置为比输入值大1个字符。我这样做的原因是,如果我不这样做,那么字符将溢出文本框,并且只有当用户放开键时,文本框才会扩展。这看起来很奇怪。这就是为什么我必须预测新字符-在每个keydown上扩大文本框,因此,在字符出现在文本框中之前。正如您可以在演示中看到的那样,这种方法看起来很不错。
然而,问题是BACKSPACE和ARROW键-它们也会在keydown上扩展文本框,而只有在keyup时,文本框大小才会得到纠正。
解决方法:
一个解决方法是手动检测BACKSPACE、SHIFT和ARROW键,并根据其进行操作:
// keydown handler
function(e) {
    var len = $(this).val().length;
    if (e.keyCode === 37 || e.keyCode === 39 ||
        e.keyCode === 16) { // ARROW LEFT or ARROW RIGHT or SHIFT key
        return;
    } else if (e.keyCode === 8) { // BACKSPACE key
        $(this).attr("size", len <= 1 ? 1 : len - 1);
    } else {
        $(this).attr("size", len === 0 ? 1 : len + 1);
    }
}

这对于BACKSPACE、SHIFT、ARROW LEFT和ARROW RIGHT起作用(并且看起来很好)。然而,我希望有一个更加稳健的解决方案。


2
大众想知道的是,“为什么在调用keyUp处理程序之前需要了解值的长度是否发生变化”? - Larry K
@Larry K 我更新了我的问题... - Šime Vidas
您是否想要检测CTRL+V(粘贴)呢? - Lukman
@Lukman 如果能够检测到跨浏览器的粘贴,那就太好了。无论如何,我的主要关注点还是按键事件。 - Šime Vidas
16个回答

33

我认为这份代码可以胜任,或者非常接近,只需要稍作调整即可。你需要记住的是,在keydownkeyup事件中,无法可靠地告诉任何可能输入的字符的任何信息:所有这些都必须在keypress处理程序中完成。关于按键事件的权威资源是http://unixpapa.com/js/key.html

您还需要考虑粘贴板操作,这段代码无法处理。您需要有一个单独的paste事件处理程序(虽然此事件在Firefox < 3.0、Opera和非常旧的WebKit浏览器中不受支持)。您需要在粘贴处理程序中设置一个计时器,因为在JavaScript中无法访问即将粘贴的内容。

function isCharacterKeyPress(evt) {
    if (typeof evt.which == "undefined") {
        // This is IE, which only fires keypress events for printable keys
        return true;
    } else if (typeof evt.which == "number" && evt.which > 0) {
        // In other browsers except old versions of WebKit, evt.which is
        // only greater than zero if the keypress is a printable key.
        // We need to filter out backspace and ctrl/alt/meta key combinations
        return !evt.ctrlKey && !evt.metaKey && !evt.altKey && evt.which != 8;
    }
    return false;
}

<input type="text" onkeypress="alert(isCharacterKeyPress(event))">

2
这是一些非常有用的信息。我已经使用了自己的解决方案更新了我的演示,它运行得足够好,但我肯定也会测试你的代码。 - Šime Vidas
@Šime:我会重申我的主要信息,即你无法可靠地通过“keydown”和“keyup”事件检测字符的任何信息。 - Tim Down
我扩展了我的解决方案。我在keydown处理程序中读取event.keyCode属性以检测非字符键。我把它放在这里:http://vidasp.net/tinydemos/dynamic-textbox.html 它工作得很好。 - Šime Vidas
7
箭头键在 evt.which 中返回一个数字。 - vsync
除了粘贴事件,我认为您还需要考虑剪切事件。(但感谢提供有用的按键事件检查器。) - Paul Lynch
显示剩余4条评论

28

这里有一个更简单的解决方案,我使用过并且很有效:

document.addEventListener('keyup', event => {
  if (String.fromCharCode(event.keyCode).match(/(\w|\s)/g)) {
    //pressed key is a char
  } else {
    //pressed key is a non-char
    //e.g. 'esc', 'backspace', 'up arrow'
  }
});

这不需要探测DOM元素(这会增加延迟和丑陋的代码)。

更新后的使用示例:


2
很有趣想知道为什么这个答案没有更高的评价...非常好的解决方案! - nlv
3
很不幸,这无法处理像表情符号这样的某些多字节字符。 - Danilo Bargen
4
虽然这绝对有助于我的个人问题,但这个答案还有一个问题:任何特殊字符如句号、逗号、冒号等都没有包括在内,尽管它们增加了字符串的长度。 - Timmiej93
3
请使用.key()代替.keyCode() - Tigerrrrr
1
这个基本上和检查长度是一样的吗?或者只需要这样写 "if (e.key.length < 2)" (我会说 ==1 但我想要包括空格)而 @NeilVarnas 也许提问者在看到这个回答之前已经接受了另一个回答。 - Raphael Morgan
显示剩余3条评论

12

我能找到的可能解决方案是检查事件中键的长度。

例如:-

<input type="text" id="testId" onkeyup="keyChecking(event)" />

<script type="text/javascript">
function keyChecking(event) {

    if (event.key.length == 1) {
        alert("key produced character " + event.key);
    } else {
        alert("Key DOES NOT produce character");

        const alphabets = "AZaz09";
        const key = event.key;
        var notEvenASymbol = false;

        for (let i = 0; i < key.length; i++) {
            var charCode = key.charCodeAt(i);
            if ((charCode >= alphabets.charCodeAt(0) && charCode <= alphabets.charCodeAt(1)) ||
                (charCode >= alphabets.charCodeAt(2) && charCode <= alphabets.charCodeAt(3)) ||
                (charCode >= alphabets.charCodeAt(4) && charCode <= alphabets.charCodeAt(5))
            ) {
                notEvenASymbol = true;
                console.log(charCode);
                break;
            }
        }

        if (notEvenASymbol) {
            alert("Key DOES NOT produce even a symbol");
        }
        console.log(event.key);

    }
}    
</script>

如果您按下任何字符/符号,则event.key将包含该字符,其长度为1。如果您按下字符V,则event.key将具有值V,但如果您按Enter键,则它将包含值Enter,如果您按Shift键,则为Shift等等。因此,如果某个键不生成字符,则其长度将大于1。

更新:

键盘中的一些特殊键会产生符号,其长度可能大于1,因此我修改了代码,以便即使它不是符号也可以发出警报。例如:长度为2。一些移动键盘上有这些符号的快捷键。

键盘中的非字符/符号键始终是字母、数字字符或两者的组合,例如:F2Shift

感谢@Vicky Chijwani关注此场景。


3
虽然这对我没有影响,但有趣的小知识:"".length 会返回2,尽管它在视觉上只有一个“字符” :P - Vicky Chijwani
非常有趣!一个想法:根据您的需求,您可能不希望像CTRL+A、CTRL+C这样的操作产生“键生成字符a”、“...c”等结果。 - Ben Philipp
哦,重要的是:可能独立存在的组合字符(例如按两次)会导致错误的负面结果,就像音调符号[´`^]一样。这很棘手,因为第一次按键无效,但第二次按键会产生这些示例的按键代码。当然,还有实际组合结果的困难。这是一场噩梦。 - Ben Philipp
如果用户按下像Ctrl+C这样的快捷键,那么 c 的长度将为1。(这并不是唯一一个没有考虑快捷键的答案。) - Qwertie

12
为了在keydown处理程序中检测按下的键是否产生单个Unicode字符,您可以使用ES6 Unicode正则表达式的u标志。我们使用KeyboardEvent.key属性,该属性返回按下的键的值。根据文档: 如果按下的键具有可打印的表示形式,则返回的值是包含键的可打印表示形式的非空Unicode字符字符串。
inputElement.addEventListener("keydown", ({ key }) => {
  if (/^.$/u.test(key)) {
    // `key` matches a single unicode character
  }
});

该解决方案无法处理粘贴操作...

这对我很有好处,因为如果按下的键是“Enter”键,则会触发false - Normal

4

好的,我想我明白了。解决方案有点巧妙,但实际上非常有效。

在按键按下时,设置一个1毫秒的setTimeout,调用一个函数来检查/更改您输入框的长度。

function checkLength() {
    var len = $("input:text").val().length;
    $("input:text").attr("size", len === 0 ? 1 : len + 1);
}

$("input:text").attr({
    "size": 1,
    "spellcheck": false
}).keydown(function() {
    setTimeout(checkLength, 1);
});
p { padding:50px; }
        
input { 
    border:1px solid gray;
    padding:6px; 
    font-size:26px;
    font-family:monospace;
    outline:none; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<input type="text"></input>

看起来它的效果非常好,特别是在你的版本无法正常工作的一些地方(例如退格键、CTRL+V或选择大量文本并按删除键)

编辑:即使使用0ms延迟的setTimeout也似乎可以工作!


@Sime:它这样做的唯一原因是因为我直接从你的网站复制了代码,那里使用了len+1。我将其更改为len。http://jsfiddle.net/rygar/e2wQM/3/ - Jeff
@Jeff 你有没有注意到在输入字符时文本框出现了奇怪的抖动?这不应该发生。 - Šime Vidas
@Sime 不,我没有。你能具体说明一下吗?在我看来,我的演示展示了与您针对字母数字字符的解决方法相同的行为,但不会出现非字母数字字符、ctrl+v等奇怪的行为。 - Jeff
@Jeff,我扩展了自己的解决方案。请在此处查看:http://vidasp.net/tinydemos/dynamic-textbox.html 它运行得很好 :) 无论如何还是谢谢你。 - Šime Vidas
@Jeff 在我的电脑上,每个浏览器中都明显出现了twitch。尝试输入不同的字符以查看它。 - Šime Vidas
显示剩余6条评论

3

在keydown函数中,你应该使用属性keyEventArgs.Key,它将返回一个数值,这个数值取决于系统。

以下链接包含了不同浏览器和操作系统的不同键码:

http://www.quirksmode.org/js/keys.html


@pst:IE不支持charCode并不是一个严重的问题,因为它在keypress事件的keyCode属性中提供了字符代码。 - Tim Down

1

我不确定所有的键盘都是这样,但我在我的键盘上发现,将“keypress”事件(不要使用keydown或keyup)与event.key结合使用,只有可打印字符才会被返回,唯一的例外是“Enter”键,它会返回单词“Enter”。

因此,我想到了以下简单的解决方案:

document.addEventListener('keypress', (event) => {
    if(event.key && event.key != 'Enter'){
      console.log('this is a character')
    }
});

这个解决方案似乎也忽略了其他答案未处理的像ctrl+c或ctrl+v这样的快捷方式。
注意:在Mozilla和Brave中进行了测试,我想看看在其他浏览器中的结果(请评论)。

1

根据其他答案,我创建了以下内容:

export function producesCharacter(event: React.KeyboardEvent<HTMLInputElement>) {
   return !event.ctrlKey && !event.altKey && event.key.length === 1;
}

由于弃用警告,其他解决方案可能无法在React中起作用。


1
我猜你正在设置一个输入字段的长度计数器,如果是这样的话,你不需要那么花哨,你可以将字段的长度赋值给一个变量,当用户达到最大长度时,只允许他们按删除或退格键,像这样:
$("input:text").keypress(function() {
var current = $(this).val().length;
if (current >= 130) {
if (e.which != 0 && e.which != 8) {
e.preventDefault();
}
}
}

你可以使用current变量来显示计数器,或者用maxlength - current来倒数显示还有多少字符剩余


在这种情况下,“current”实际上反映了新字符输入之前文本字段的长度,因此您的计数器将比应该少一个(直到键盘松开)。 - Jeff
不行。我真的需要知道,对于每个按下的键,是否会增加文本框值的长度。 - Šime Vidas
其他键如home、end、copy、paste、paging、up、down等呢? - Sam Plus Plus

1

你的目标是保持文本框比输入的文本更大。

我会通过在文本框中预留两个额外字符的空间(而不是一个)来实现这一目标。然后:

// pseudo-code.... 
old_len = textbox.value.length
keyUp function() {
  var new_len = textbox.value.length
  if (new_len != old_len) {
    old_len = new_len
    textbox.style.size = new_len + 2 // pseudo code.
  }
}

以上的优点在于您不需要深入到按键代码的地下世界。

不,文本框必须保持与输入相同的大小。我只在按键按下和松开事件之间扩大文本框的大小,因为我预期会有新字符输入。我已更新我的演示 - 请查看其中的解决方法 - 这就是我想要的效果。但是,在我的解决方法中,我硬编码了特殊键。 - Šime Vidas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接