使用JQuery立即检测<input type="text">的所有更改

431

一个 <input type="text"> 元素的值可以通过多种方式改变,包括:

  • 按键事件
  • 复制/粘贴
  • 使用 JavaScript 修改
  • 由浏览器或工具栏自动完成

我希望在输入框的内容发生变化时,立即调用我的 JavaScript 函数(并传递当前输入框的值),而不仅是在输入框失去焦点时调用。

我正在寻找最干净、最健壮的跨所有浏览器的方法(最好使用 jQuery)。


1
请参考以下链接:https://dev59.com/V3RB5IYBdhLWcg3wl4Sc 和 https://dev59.com/VXI-5IYBdhLWcg3wc3-w。 - Crescent Fresh
1
https://dev59.com/mnM_5IYBdhLWcg3w8IHR - andres descalzo
16个回答

363

这段 jQuery 代码使用 .bind() 捕获任何元素的即时更改,并应该适用于所有浏览器:

 $('.myElements').each(function() {
   var elem = $(this);

   // Save current value of element
   elem.data('oldVal', elem.val());

   // Look for changes in the value
   elem.bind("propertychange change click keyup input paste", function(event){
      // If value has changed...
      if (elem.data('oldVal') != elem.val()) {
       // Updated stored value
       elem.data('oldVal', elem.val());
    
       // Do action
       ....
     }
   });
 });

不过请注意,.bind()在jQuery 3.0版本中已被弃用。任何使用jQuery 1.7或更高版本的人应该改用.on()


24
供大家参考,input是HTML5事件,我认为应该覆盖所有现代浏览器(MDN参考文档)。 keyup应该可以捕获其余事件,但存在轻微延迟(input由按键按下时触发)。 propertychange是专有的微软事件,我不确定是否实际上需要使用paste - Jo Liss
80
我对吗?这段代码无法处理通过JavaScript更改文本的情况,比如 document.getElementById('txtInput').value = 'some text'; - Mehmet Ataş
6
Mehmet,该代码不是为了捕捉通过JS更改的值。 - phatmann
17
@MehmetAtaş的部分观点正确。 这里关于“input”事件的参考文献(http://help.dottoro.com/ljhxklln.php)指出,当内容通过JavaScript修改时,该事件不会触发。 但是,“onpropertychange”事件会在通过JS进行修改时触发(请参见此处:http://help.dottoro.com/ljufknus.php)。因此,当内容通过JS在IE上进行修改时,此代码将有效,但在其他浏览器上不起作用。 - Vicky Chijwani
2
你可以在 input 上触发自定义事件,当 propertychange、keyup 等事件发生时,然后在任何地方使用它。 - Glorious Kale
显示剩余8条评论

130

jQuery >= 1.7 的实时解决方案是on

$("#input-id").on("change keyup paste", function(){
    dosomething();
})

如果你也想检测“点击”事件,只需:

$("#input-id").on("change keyup paste click", function(){
    dosomething();
})

如果您使用的是 jQuery <= 1.6 版本,只需使用 bindlive 替代 on


3
这个有一个缺点。如果你用Tab键离开输入框,即使内容没有被编辑过,它也会触发keyup事件。 - Pawka
3
如何检测datetimepicker何时填充了#input-id?更改和粘贴等事件都无效。 - Pathros
太好了!我用jquery 1.8.3试过了,它可以工作。我仍然有一些livequery()代码需要替换,所以我不能完全升级到1.9.1甚至更高版本。我知道这很老旧,但整个网站也是这样。 - Asle
请注意:如果使用键盘选择无效日期(如2月30日),则这些事件将以空值触发。 - Olmstov
请注意,如果值是通过JavaScript更改的,则这些事件不会触发。 - thdoan

120

很遗憾,我认为setInterval是最佳选择:

<input type=text id=input_id />
<script>
setInterval(function() { ObserveInputValue($('#input_id').val()); }, 100);
</script>

这是最简单的解决方案,只需1行代码。它也是最健壮的,因为您无需担心所有不同的事件/方式input可以获得值。

使用“setInterval”的缺点似乎不适用于此情况:

  • 100毫秒的延迟? 对于许多应用程序来说,100毫秒已足够快。
  • 浏览器上的额外负载? 通常,在页面上添加大量重量级的setInterval是不好的。但在这种特殊情况下,添加的页面负载是无法检测到的。
  • 无法扩展到许多输入? 大多数页面没有超过一小部分的输入,您可以在同一个setInterval中嗅探所有输入。

27
在使用Nintendo 3DS浏览器(版本/1.7552.EU)时,这似乎是检测表单输入字段更改的唯一方法。 - Johan
5
注意:过载可以得到缓解。如果您在输入获得焦点时开始setInterval,当输入失去焦点时清除setInterval。 - Tebe
11
为什么要创建一个没有必要不断运行的100毫秒时间间隔?事件,人们。利用它们。 - Gavin
19
@Gavin,在这种情况下,JS缺少事件..请找出一组事件,可以识别用户输入、用户粘贴、用户剪切、JavaScript插入、JavaScript移除、表单自动完成、隐藏表单、不显示的表单。 - Naramsim
2
@Gavin,JS似乎没有一个在每次输入更改时都会触发的onchanged事件。(只需执行document.getElementById(name)。value = Math.random()即可看到没有任何事件触发解决方案有效。) - Joeri
显示剩余12条评论

61
绑定 input 事件在大多数正常的浏览器中运行良好。IE9也支持,但实现存在缺陷(删除字符时不会触发事件)。
使用 jQuery 版本1.7+,on 方法非常有用,可以这样绑定事件:
$(".inputElement").on("input", null, null, callbackFunction);

13
如您在此测试中所见,oninput事件无法与input[type=reset]或JavaScript编辑一起使用。 - Yukulélé
2
@Yukulélé,至少我们可以更容易地解决这个问题,而不是尝试检测所有可能的用户操作。 - Pacerier

17

很遗憾,没有事件或一组事件与您的条件相匹配。键盘按键和复制/粘贴可以通过keyup事件处理。使用JS进行更改要困难得多。如果您控制设置文本框的代码,则最好修改它以直接调用您的函数或在文本框上触发用户事件:

// Compare the textbox's current and last value.  Report a change to the console.
function watchTextbox() {
  var txtInput = $('#txtInput');
  var lastValue = txtInput.data('lastValue');
  var currentValue = txtInput.val();
  if (lastValue != currentValue) {
    console.log('Value changed from ' + lastValue + ' to ' + currentValue);
    txtInput.data('lastValue', currentValue);
  }
}

// Record the initial value of the textbox.
$('#txtInput').data('lastValue', $('#txtInput').val());

// Bind to the keypress and user-defined set event.
$('#txtInput').bind('keypress set', null, watchTextbox);

// Example of JS code triggering the user event
$('#btnSetText').click(function (ev) {
  $('#txtInput').val('abc def').trigger('set');
});

如果您无法控制该代码,可以使用setInterval()来“监视”文本框的更改:

// Check the textbox every 100 milliseconds.  This seems to be pretty responsive.
setInterval(watchTextbox, 100);

这种主动监控不会“立即”捕捉到更新,但它似乎足够快,没有察觉到的延迟。正如DrLouie在评论中指出的那样,如果您需要监视大量输入,则该解决方案可能无法很好地扩展。您始终可以调整setInterval()的第二个参数来更或少频繁地检查。


1
如果有人需要跟踪50个字段会怎样? - DoctorLouie
然而,如果您只需要监视一个字段,那么没有理由不采用这种修复方法。 - DoctorLouie
1
是的,我的情况只有一个字段,但我不认为这对于许多字段而言不能很好地工作。最有效的方法可能是只使用1个setTimeout来检查所有输入。 - Dustin Boswell
1
@Douglas:没有JavaScript事件处理这种情况 - 更正:IE可以使用input.onpropertychange处理它,而FF2+、Opera 9.5、Safari 3和Chrome 1可以使用input.__defineSetter__('value', ...)处理它(虽然它被记录为在DOM节点上不起作用,但我可以验证它有效)。唯一与IE实现不同的是IE不仅在编程设置时而且在键事件期间也触发处理程序。 - Crescent Fresh
我确实觉得setInterval有点像最后的手段。因此,如果您可以修改更新文本框的JS代码,我添加了一个可供使用的替代解决方案。 - Annabelle
显示剩余14条评论

13
以下是不使用jQuery的解决方案(这些天它真的很过时,也不必要):
通过事件“input”,您可以查找任何类型的更改:删除、后退、粘贴、输入等任何更改输入值的操作。该“input”事件直接与文本输入相关联。无论以任何方式更改文本,都会触发“input”事件。
以下是代码示例: ```js document.getElementById("myInput").addEventListener("input", function(){ // Do something whenever the input changes }); ```

document.querySelector("#testInput").addEventListener("input", test);

function test(e) {
    var a = document.getElementById('output');
    a.innerText += "Detected an Update!\n";
}
<input id="testInput">
<br>
<a id="output"></a>


1
@Tylor,我提供了一个更简单的解决方案。jQuery有太多XY问题的例子。如果人们想要捷径,他们真的不应该编程。特别是当这些捷径会更加耗费性能或资源时...此外,这个答案是2019年的。现在投诉已经晚了,因为它已经在这个网络上活跃上传了将近三年。 - Mister SirCode
1
当然,这种方法无法处理 OP 概述的特定情况,因为通过 JavaScript 进行的更改不会触发它。 - Kevin B

6

如果你不喜欢其他答案,这里有一个稍微不同的解决方案:

var field_selectors = ["#a", "#b"];
setInterval(function() { 
  $.each(field_selectors, function() { 
    var input = $(this);
    var old = input.attr("data-old-value");
    var current = input.val();
    if (old !== current) { 
      if (typeof old != 'undefined') { 
        ... your code ...
      }
      input.attr("data-old-value", current);
    }   
  }   
}, 500);

考虑到无法使用单击和按键松开来捕获上下文菜单的粘贴操作。

这个答案对我来说最有效。在使用以下代码之前,我遇到了一些选择器的问题: $("input[id^=Units_]").each(function() { - robr

4

在任意位置添加以下代码,这将解决问题。

var originalVal = $.fn.val;
$.fn.val = function(){
    var result =originalVal.apply(this,arguments);
    if(arguments.length>0)
        $(this).change(); // OR with custom event $(this).trigger('value-changed');
    return result;
};

这篇文章中找到了解决方案,涉及到IT技术。

3

实际上,我们不需要设置循环来检测JavaScript的更改。我们已经为要检测的元素设置了许多事件侦听器。只需要触发任何无害的事件就可以完成工作。

$("input[name='test-element']").on("propertychange change click keyup input paste blur", function(){
console.log("yeh thats worked!");
});

$("input[name='test-element']").val("test").trigger("blur");

当然,只有在您完全控制项目中的JavaScript更改时才能使用此功能。


3

虽然这个问题已经发布了10年,但我认为仍需要一些改进。因此,以下是我的解决方案。

$(document).on('propertychange change click keyup input paste', 'selector', function (e) {
    // Do something here
});

这种解决方案唯一的问题是,如果值从JavaScript更改,比如$('selector').val('some value'),它就不会触发。您可以在从JavaScript更改值时对选择器触发任何事件。
$(selector).val('some value');
// fire event
$(selector).trigger('change');

或者在一行中

$(selector).val('some value').trigger('change');

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