如何在onKeyPress事件中获取输入文本框的文本?

103

我正在尝试在用户输入文本时获取文本框中的文本(jsfiddle playground):

function edValueKeyPress() {
    var edValue = document.getElementById("edValue");
    var s = edValue.value;

    var lblValue = document.getElementById("lblValue");
    lblValue.innerText = "The text box contains: " + s;

    //var s = $("#edValue").val();
    //$("#lblValue").text(s);    
}
<input id="edValue" type="text" onKeyPress="edValueKeyPress()"><br>
<span id="lblValue">The text box contains: </span>

除了 onKeyPress 事件中的输入文本框的总是在更改之前的值外,代码没有错误:

enter image description here

问题:如何在 onKeyPress 事件期间获取文本框的文本?

额外信息

HTML DOM 中与"用户正在键入"有关的事件有三个:

  • onKeyDown
  • onKeyPress
  • onKeyUp

Windows中,当用户按住一个键并开始重复时,WM_Key 消息的顺序变得重要:

  • WM_KEYDOWN('a') - 用户已按下 A
  • WM_CHAR('a') - 已从用户接收到一个 a 字符
  • WM_CHAR('a') - 已从用户接收到一个 a 字符
  • WM_CHAR('a') - 已从用户接收到一个 a 字符
  • WM_CHAR('a') - 已从用户接收到一个 a 字符
  • WM_CHAR('a') - 已从用户接收到一个 a 字符
  • WM_KEYUP('a') - 用户已释放 A

将导致五个字符出现在文本控件中:aaaaa

重要的是,您要响应重复的 WM_CHAR 消息。否则,当按下键时会错过事件。

HTML中情况略有不同:

  • onKeyDown
  • onKeyPress
  • onKeyDown
  • onKeyPress
  • onKeyDown
  • onKeyPress
  • onKeyDown
  • onKeyPress
  • onKeyDown
  • onKeyPress
  • onKeyUp

Html 在每次按键重复时都会触发 onKeyDownonKeyPress 事件,而只有在用户释放键时才会引发 onKeyUp 事件。

要点:

  • 我可以响应onKeyDownonKeyPress,但两者在input.value更新之前都会被触发。
  • 我无法响应onKeyUp,因为它不会随着文本框中的文本变化而发生。

问题:如何在onKeyPress期间获取文本框中的文本?

Bonus 阅读材料


1
我认为你需要在返回值之前将当前按键附加到值上;值在 keyUp 时更新,但数据在 keyDown 时可用。 - Evan Davis
键盘抬起事件对我有效,像这样的事情你不需要 jQ,除非你想添加臃肿。 - Ryan B
只需要使用 onKeyUp 就足以完成你的任务。 - dmytro
仅处理 onKeyUp 无法显示文本框中实时发生的更改。 - Ian Boyd
如果你想按住任何键并在文本框中获取结果,当然你应该同时使用onKeyPress和onKeyDown事件Jonathan M的fiddle,但是如果你想在不按住键的情况下获得结果,只需要使用onKeyUp 我的fiddle就足够了。 - dmytro
12个回答

57

保持简单。同时使用onKeyPress()onKeyUp()

<input id="edValue" type="text" onKeyPress="edValueKeyPress()" onKeyUp="edValueKeyPress()">

这将处理获取最新的字符串值(在键盘抬起后)并在用户按住键时进行更新。

jsfiddle: http://jsfiddle.net/VDd6C/8/


由于arnaund576875的答案不得不放弃并回退到onKeyUp(这样做就打败了使用onKeypress的目的),因此这是最简单、最清晰和最接近的答案。只有最仔细观察的用户才会注意到反馈与他们输入的内容不匹配。 - Ian Boyd
@Ian,很高兴能帮忙。你所说的“反馈不符合他们输入的内容”是什么意思?需要进行调整吗? - Jonathan M
我现在记得我想说的了!我所指的反馈是将<input>元素着色为红色、绿色或提供其他反馈,告诉用户他们输入的内容是好还是坏。虽然您接受的答案仍然存在“偏移一个字符”的问题:只有最敏锐的用户才会注意到反馈与他们输入的内容不匹配。实际上,由于偏移一个字符的错误只发生在按键重复时(即他们按住键),除了我之外,没有人会注意到这个问题。 - Ian Boyd

34

处理input事件是一种一致的解决方案:它被支持在所有现代浏览器中用于textareainput元素,并且它会在你需要它时准确地触发:

function edValueKeyPress() {
    var edValue = document.getElementById("edValue");
    var s = edValue.value;

    var lblValue = document.getElementById("lblValue");
    lblValue.innerText = "The text box contains: " + s;
}
<input id="edValue" type="text" onInput="edValueKeyPress()"><br>
<span id="lblValue">The text box contains: </span>

我会稍微改写一下:

function showCurrentValue(event)
{
    const value = event.target.value;
    document.getElementById("label").innerText = value;
}
<input type="text" onInput="showCurrentValue(event)"><br>
The text box contains: <span id="label"></span>


19

在 onKeyPress 期间,输入文本框的值始终是更改之前的值。

这是有意为之的:这使事件监听器可以取消按键。

如果事件监听器取消了事件,则该值不会被更新。 如果事件未被取消,则该值将被更新,但是在事件监听器被调用后更新。

要获取字段值更新后的值,请安排一个函数在下一个事件循环上运行。通常的做法是使用延迟为0setTimeout函数:

$('#field').keyup(function() {
    var $field = $(this);

    // this is the value before the keypress
    var beforeVal = $field.val();

    setTimeout(function() {

        // this is the value after the keypress
        var afterVal = $field.val();
    }, 0);
});

在这里试试:http://jsfiddle.net/Q57gY/2/

编辑:一些浏览器(例如 Chrome)不会为退格键触发keypress事件;已在代码中将keypress更改为keyup。


即将发布这个。这是我做的jsFiddle - kapa
你是对的,Chrome似乎不会在按下退格键时触发keypress事件;已更新。 - Arnaud Le Blanc
@Ian,OP是否希望在按住键时更新?我不太确定这是否是要求。然而,此解决方案没有该功能。 - Jonathan M
要明确的是:不触发退格键的按键事件符合标准:“产生字符值的按键”-对于其他按键使用“keydown”(更改前,可取消)或“keyup”(更改后)。 - sebilasse

5

保持紧凑。
每次按键时,函数edValueKeyPress()都会被调用。
您还在该函数中声明和初始化了一些变量,这会减慢进程并需要更多的CPU和内存。
您可以简单地使用从简单替换派生的此代码。

function edValueKeyPress()
{
    document.getElementById("lblValue").innerText =""+document.getElementById("edValue").value;
}

那就是你想要的,而且速度更快!

3
迄今为止,没有一个答案提供了完整的解决方案。有很多问题需要解决:
  1. 并非所有按键都会传递到 keydownkeypress 处理程序(例如退格和删除键被一些浏览器抑制)。
  2. 处理 keydown 不是一个好主意。有些情况下,按下键并不会导致按键操作!
  3. setTimeout() 类型的解决方案在 Google Chrome/Blink web 浏览器下会被延迟,直到用户停止输入。
  4. 鼠标和触摸事件可以用于执行剪切、复制和粘贴等操作。这些事件不会触发键盘事件。
  5. 根据输入方法的不同,浏览器可能直到用户离开该字段后才会传递元素已更改的通知。
更正确的解决方案将处理 keypresskeyupinputchange 事件。
示例:
<p><input id="editvalue" type="text"></p>
<p>The text box contains: <span id="labelvalue"></span></p>

<script>
function UpdateDisplay()
{
    var inputelem = document.getElementById("editvalue");
    var s = inputelem.value;

    var labelelem = document.getElementById("labelvalue");
    labelelem.innerText = s;
}

// Initial update.
UpdateDisplay();

// Register event handlers.
var inputelem = document.getElementById("editvalue");
inputelem.addEventListener('keypress', UpdateDisplay);
inputelem.addEventListener('keyup', UpdateDisplay);
inputelem.addEventListener('input', UpdateDisplay);
inputelem.addEventListener('change', UpdateDisplay);
</script>

调试工具:

http://jsfiddle.net/VDd6C/2175/

处理所有四个事件可以捕获所有边缘情况。在处理用户输入时,应考虑到所有类型的输入方法,并验证跨浏览器和跨设备的功能。上述代码已在我所拥有的桌面上的Firefox、Edge和Chrome以及移动设备上进行了测试。


为什么不仅使用 input 事件? - YakovL
"input" 不总是触发。没有单个事件涵盖所有情况。 - CubicleSoft
如果您能更详细地描述“not always”(不总是)的情况,就像您在答案中为 keypress 规定的那样,那将非常好。 - YakovL

3
<asp:TextBox ID="txtMobile" runat="server" CssClass="form-control" style="width:92%;  margin:0px 5px 0px 5px;" onkeypress="javascript:return isNumberKey(event);" MaxLength="12"></asp:TextBox>

<script>
    function isNumberKey(evt) {
        var charCode = (evt.which) ? evt.which : event.keyCode;
        if (charCode > 31 && (charCode < 48 || charCode > 57)) {
            return false;
        }
        return true;
    }
</script>

1
使用event.key,我们可以获取输入到HTML输入文本框中的值。以下是代码。

function checkText()
{
  console.log("Value Entered which was prevented was - " + event.key);
  
  //Following will prevent displaying value on textbox
  //You need to use your validation functions here and return value true or false.
  return false;
}
<input type="text" placeholder="Enter Value" onkeypress="return checkText()" />


当文本通过非按键方式更改(剪切,粘贴,删除所选等)时,这种方法就会失效。 - Ian Boyd
@IanBoyd 是的,同意。为了保持示例简单,我只实现了按键验证。 - vibs2006

1
在你的keyPress事件处理程序中,写入:

easy...

void ValidateKeyPressHandler(object sender, KeyPressEventArgs e)
{
    var tb = sender as TextBox;
    var startPos = tb.SelectionStart;
    var selLen= tb.SelectionLength;

    var afterEditValue = tb.Text.Remove(startPos, selLen)
                .Insert(startPos, e.KeyChar.ToString()); 
    //  ... more here
}

如果 e.KeyCharBackspace 键,会发生什么?或者是 Delete 键?或者是 Ctrl+A - Ian Boyd
这是 JavaScript 吗?voidobject?首字母大写的方法? - YakovL

1

通常我会将字段的值(即在更新之前)与与键事件相关联的键连接起来。以下示例使用最近的JS,因此需要对旧版本IE进行调整。

最近的JS示例

document.querySelector('#test').addEventListener('keypress', function(evt) {
    var real_val = this.value + String.fromCharCode(evt.which);
    if (evt.which == 8) real_val = real_val.substr(0, real_val.length - 2);
    alert(real_val);
}, false);

支持旧版IE浏览器示例

//get field
var field = document.getElementById('test');

//bind, somehow
if (window.addEventListener)
    field.addEventListener('keypress', keypress_cb, false);
else
    field.attachEvent('onkeypress', keypress_cb);

//callback
function keypress_cb(evt) {
    evt = evt || window.event;
    var code = evt.which || evt.keyCode,
        real_val = this.value + String.fromCharCode(code);
    if (code == 8) real_val = real_val.substr(0, real_val.length - 2);
}

[编辑 - 这种方法默认情况下会禁用像退格键、CTRL+A这样的按键。上面的代码适应了前者,但需要进一步调整才能允许后者和其他一些情况。请参见Ian Boyd在下面的评论中的说明。]


1
@bažmegakapa 或者使用 delete 键,或者如果用户按下不修改内容的键(Ctrl+A),或者如果用户选择一些文本,然后按下 a 来替换子集。这是一个好主意,Utkanos,但是很脆弱。 - Ian Boyd
1
同意。我会保留它,因为它还有一些价值,但正如你所说,你必须考虑这些警告的情况。 - Mitya

1

因此,每个事件都有优缺点。事件onkeypressonkeydown不会检索最新值,并且在某些浏览器中,onkeypress不会针对非可打印字符触发。onkeyup事件无法检测按住多个字符的键。

这有点hacky,但可以做类似以下操作

function edValueKeyDown(input) {
    var s = input.value;

    var lblValue = document.getElementById("lblValue");
    lblValue.innerText = "The text box contains: "+s;

    //var s = $("#edValue").val();
    //$("#lblValue").text(s);    
}

<input id="edValue" type="text" onkeydown="setTimeout(edValueKeyDown, 0, this)" />

似乎能够处理所有领域中的最佳选择。

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