429

There are many ways the value of a <input type="text"> can change, including:

  • keypresses
  • copy/paste
  • modified with JavaScript
  • auto-completed by browser or a toolbar

I want my JavaScript function to be called (with the current input value) any time it changes. And I want it to be called right away, not just when the input loses focus.

I'm looking for the cleanest and most robust way to do this across all browsers (using jQuery preferably).

Dustin Boswell
  • 6,114
  • 7
  • 28
  • 26
  • 1
    See http://stackoverflow.com/questions/574941/best-way-to-track-onchange-as-you-type-in-input-typetext and http://stackoverflow.com/questions/1847893/js-events-hooking-on-value-change-event-on-text-inputs – Crescent Fresh Dec 22 '09 at 19:05
  • 1
    http://stackoverflow.com/questions/1159046/jquery-change-event-on-an-input-element-any-way-to-retain-previous-value – andres descalzo Dec 22 '09 at 19:10

16 Answers16

363

This jQuery code uses .bind() to catch immediate changes to any element, and should work across all browsers:

 $('.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
       ....
     }
   });
 });

However, note that .bind() was deprecated in jQuery version 3.0. Anyone using jQuery version 1.7 or newer should use .on() instead.

TylerH
  • 20,799
  • 66
  • 75
  • 101
phatmann
  • 18,161
  • 7
  • 61
  • 51
  • 23
    For everyone's reference, `input` is the HTML5 event that I believe should cover all modern browsers ([MDN reference](https://developer.mozilla.org/en/DOM/window.oninput)). `keyup` should catch the rest, though with a slight delay (`input` is triggered on key down). `propertychange` is a proprietary MS event, and I'm not sure if `paste` is actually necessary. – Jo Liss Apr 28 '12 at 19:43
  • 1
    It is important to note that prior to IE 9, onpropertychange does not bubble. This means it is impossible to bind to this event using `.live()` (or `.on()` in useful cases) unless you bind it directly to the input element itself. – Eli Sand May 05 '12 at 03:29
  • 79
    Am I wrong or this does not handle when text changed via javascript like `document.getElementById('txtInput').value = 'some text';` – Mehmet Ataş Aug 09 '12 at 13:34
  • Not working here when input changed by javascript either (FF 15). – Berend de Boer Oct 09 '12 at 07:22
  • 6
    Mehmet, the code is not meant to catch values changed via JS. – phatmann Nov 17 '12 at 11:22
  • 17
    @MehmetAtaş is _partly_ correct. The reference for the `input` event [here](http://help.dottoro.com/ljhxklln.php) states that it does not fire when the contents are modified via JavaScript. However, the `onpropertychange` event _does_ fire on modification via JS (see [here](http://help.dottoro.com/ljufknus.php)). So this code will work when the contents are modified via JS on IE, but will not work on other browsers. – Vicky Chijwani Dec 25 '12 at 10:28
  • 2
    You can trigger a custom event on the input when propertychange, keyup etc , and then use it anywhere. – Glorious Kale May 17 '13 at 11:15
  • 1
    I can't trigger a custom event when it's about a large 3rd party code without diving into it. And after that there might be problems if I update this 3rd party code and my changes get erased or start to cause some bugs. – Gherman Jun 11 '14 at 13:21
  • Will this `input` event be fired, as soon as the content of the input is changed using javascript? – Axel Nov 04 '14 at 17:46
  • @Axel see other comments above. My code was not meant to catch changes via JS, but rather via user interaction. Looking at the OP's question, I see they did say "modified with JavaScript" so my solution is not 100% correct. Per [this answer](http://stackoverflow.com/a/15491324/201828) I just added the `change` event, which I believe handles this case as of jQuery 1.9. – phatmann Nov 05 '14 at 18:17
  • Wow and there's me thinking JQuery is supposed to shorten your code, JS's "oninput" would achieve this in much less syntax! – Christopher Grigg Jul 27 '15 at 04:24
  • 1
    @ChristopherGrigg, `input` only works in HTML5. For older browsers you need the other events. – phatmann Jul 28 '15 at 12:21
  • none of these events are triggered when the value changes based on angular binding :( – Sonic Soul Feb 13 '18 at 18:02
130

A real-time solution for jQuery >= 1.7 is on

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

if you also want to detect "click" event, just:

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

if you're using jQuery <= 1.6, just use bind or live instead of on.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Felix
  • 1,910
  • 1
  • 15
  • 11
  • 3
    This has a drawback. If you leave input field with tab key - it will throw keyup event even if content was not edited. – Pawka Aug 22 '14 at 10:33
  • 3
    How to detect when datetimepicker fills the #input-id? None of the events (change, paste) work. – Pathros Jun 24 '15 at 15:33
  • This is great! I tried it with jquery 1.8.3 and it works. I still have some livequery() code I have to replace so I can't quite upgrade to even 1.9.1. I know it is old but so is the whole website. – Asle Oct 11 '16 at 19:52
  • FYI: These events will fire with a blank value if the keyboard is used to select a date that is not valid. ie. Feb 30. https://stackoverflow.com/questions/48564568/html5-input-type-date-input-event-incorrectly-fires-with-empty-value/48564701#48564701 – Olmstov Feb 01 '18 at 14:20
  • Note that these events won't fire if the value was changed via JavaScript. – thdoan Dec 31 '22 at 22:48
120

Unfortunately, I think setInterval wins the prize:

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

It's the cleanest solution, at only 1 line of code. It's also the most robust, since you don't have to worry about all the different events/ways an input can get a value.

The downsides of using 'setInterval' don't seem to apply in this case:

  • The 100ms latency? For many applications, 100ms is fast enough.
  • Added load on the browser? In general, adding lots of heavy-weight setIntervals on your page is bad. But in this particular case, the added page load is undetectable.
  • It doesn't scale to many inputs? Most pages don't have more than a handful of inputs, which you can sniff all in the same setInterval.
Dustin Boswell
  • 6,114
  • 7
  • 28
  • 26
  • Given that some other answers supposedly only fail to catch modifications to the `input` via Javascript, wouldn't an alternative be to catch all the events that modification by the user can trigger, and also have all modifications of input elements by code use a wrapper function that explicitly triggers a change event on the input element? This would have its own downsides (risk of carelessly not using the wrapper function; won't work for a library / plugin on someone else's page; requires changing existing code) but would eliminate the latency; perhaps list both approaches and compare? – Mark Amery Jun 23 '13 at 12:11
  • 1
    There are other code snippets posted here, but here is one based on this post: http://codepen.io/chrisfargen/pen/qbyaG – chrisfargen Jul 03 '13 at 02:44
  • 27
    This appears to be the only way to detect changes to an input field in a form when using the Nintendo 3DS browser (Version/1.7552.EU). – Johan Jul 03 '13 at 11:39
  • 1
    This is the only one that also works with AngularJS's data binding. http://paste.laravel.com/GBR – Michael J. Calkins Aug 05 '13 at 03:05
  • Note that `$('#input_id').val()` is definitively not one line of code and it's going to search for #input_id each time the timer times out! – Alexis Wilke Jun 23 '14 at 00:51
  • I actually want to have a solution that does NOT trigger when I make changes through javascript because I need to only know when the user made a change. – David Nouls Sep 11 '14 at 14:32
  • 5
    Note: Overload can be eased. If you start setInterval when input gets focus and clearInterval when input lost focus – Tebe Oct 28 '14 at 09:44
  • 11
    Why would you create a constantly running 100ms interval where there's no reason for it to be constantly running? EVENTS people. Use them. – Gavin Nov 08 '14 at 03:50
  • 19
    @Gavin, in this particular case JS lacks of events.. find us a sets of events that can recognize, user inputs, user pastes, user cuts, javascript insertions, javascript removals, auto-completitions of forms, hidden forms, none-displayed forms. – Naramsim Jun 30 '15 at 11:31
  • 2
    @Gavin JS does not seem to have a onchanged event that is triggered EVERYTIME an input is changed. ( just do a document.getElementById(name).value = Math.random() ) to see none of the event trigger solutions work. – Joeri Oct 07 '15 at 17:03
  • 2
    Perhaps this approach can help to https://gist.github.com/inter-coder/d674758f727fa866f9e9 ,solution cover the case when the element no longer exists, then extinguish interval – Dusan Krstic Oct 30 '15 at 12:21
  • "ReferenceError: ObserveInputValue is not defined" and a google search does not give an example of using this. Because a large JS library is changing this field, and as it may be updated (so modifying it is unwise) I used setInterval, and kept checking the value of the field. Crazy there is no way to detect JS changing a field value - or please enlighten us if this is incorrect. – JosephK Feb 06 '16 at 09:25
  • @JosephK you are the one who implements ObserveInputValue. Do you want it to run a validation, alert, whatever? You have to decide. – Taugenichts May 23 '16 at 20:33
  • that was the only solution that worked for me in my case. I have a select that was created with a php function, not even binding on $(document)... helped. – AJ_83 Sep 13 '16 at 09:26
  • @Копать_Шо_я_нашел This will be a problem if input value changed with $(selector).val('new val'), because on focus out timer was killed and as result this change is missed. Moreover changes to other inputs while you focused on one will be missed to. – Nikita Jun 08 '17 at 13:40
  • @Nikita so you should trigger change on your own or trigger element getting focus and losing it. I ran into such problem three years ago, I guess I tried to solve the problem from the wrong side then – Tebe Jun 08 '17 at 14:00
  • @Tebe as @Nikita said, using focus will ignore all value changes made by javascript. The whole point of using `setInterval` was to detect changes by javascript in cases where the values are changed by some 3rd party javascript we have no control over - or by our own script where we don't want to inject a *change* trigger after every code of value change. If not for that, the other answers using events would work just fine. – Jay Dadhania Dec 07 '19 at 21:01
60

Binding to the input event seems to work fine in most sane browsers. IE9 supports it too, but the implementation is buggy (the event is not fired when deleting characters).

With jQuery version 1.7+ the on method is useful to bind to the event like this:

$(".inputElement").on("input", null, null, callbackFunction);
Dharman
  • 30,962
  • 25
  • 85
  • 135
HRJ
  • 17,079
  • 11
  • 56
  • 80
  • 13
    ```oninput``` event don't works with ```input[type=reset]``` or javascript editing as you can see in this test: http://codepen.io/yukulele/pen/xtEpb – Yukulélé Apr 15 '13 at 12:45
  • 2
    @Yukulélé, At least we can work around that more easily than trying to detect all possible **user** action. – Pacerier Jul 22 '14 at 00:17
17

Unfortunately there is no event or set of events that matches your criteria. Keypresses and copy/paste can both be handled with the keyup event. Changes through JS are trickier. If you have control over the code that sets the textbox, your best bet is to modify it to either call your function directly or trigger a user event on the textbox:

// 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');
});

If you don't have control over that code, you could use setInterval() to 'watch' the textbox for changes:

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

This sort of active monitoring won't catch updates 'immediately', but it seems to be fast enough that there is no perceptible lag. As DrLouie pointed out in comments, this solution probably doesn't scale well if you need to watch lots of inputs. You can always adjust the 2nd parameter to setInterval() to check more or less frequently.

Roatin Marth
  • 23,589
  • 3
  • 51
  • 55
Annabelle
  • 10,596
  • 5
  • 27
  • 26
  • FYI there are actually a slew of events that can be combined to match the OPs criteria in varying degrees across browsers (see links in first question comment). This answer does not use any of said events, and ironically will probably work just as well in all browsers, albeit with a slight lag ;) – Crescent Fresh Dec 22 '09 at 19:20
  • The OP wants to catch changes to the textbox via JavaScript (e.g. `myTextBox.value = 'foo';`. No JavaScript events handle that situation. – Annabelle Dec 22 '09 at 19:25
  • 1
    What if someone had 50 fields to keep tabs on? – DoctorLouie Dec 22 '09 at 19:28
  • Yet, if you only have one field to monitor there's no reason not to go with this fix. – DoctorLouie Dec 22 '09 at 19:30
  • Then I'd point them to idrumgood's solution. The OP only mentioned one field, and my impression was the JS code updating the textbox was out of his control. – Annabelle Dec 22 '09 at 19:31
  • 1
    Yes, I only have 1 field in my case, but I don't see why this wouldn't work reasonably well for a number of fields. It would probably be most efficient to just have 1 setTimeout that checks all inputs. – Dustin Boswell Dec 22 '09 at 19:37
  • 1
    @Douglas: *No JavaScript events handle that situation* - Correction: IE can handle it with `input.onpropertychange`, and FF2+, Opera 9.5, Safari 3, and Chrome 1 can handle it with `input.__defineSetter__('value', ...)` (which, although it's documented as not functional on DOM nodes, I can verify it works). The only divergence from IE's implementation is IE fires the handler not only on programmatic setting but also during key events. – Crescent Fresh Dec 22 '09 at 19:39
  • Well I do feel that `setInterval` is sort of a weapon of last resort. So I added an alternate solution you could use if you can modify the JS code that updates the textbox. – Annabelle Dec 22 '09 at 19:39
  • CF: interesting, hadn't thought of going the setter route! – Annabelle Dec 22 '09 at 19:41
  • Nice use of custom events! If teh OP can modified the source though, what is the advantage of using this approach? – Roatin Marth Dec 22 '09 at 19:43
  • Roatin: which approach are you referring to? Custom events versus calling the watch function directly? I like the custom event there because it keeps the setting code less aware of the details. If you meant the `setInterval` approach, there's no advantage and I wouldn't use it. – Annabelle Dec 22 '09 at 19:47
  • Can someone explain all the negatives of using setInterval in this case? The 100ms delay is perfectly acceptable in my case. – Dustin Boswell Dec 22 '09 at 19:52
  • Dustin: if you can modify the code that sets the textbox value, go with events. If not, setInterval should be fine for you. You just have to be careful with it because you're putting an extra constant load on the browser. Too much of that can degrade your site's performance especially in older hardware. – Annabelle Dec 22 '09 at 19:59
  • Douglas: how much extra load? On an "average" computer, would the setInterval() add 0.1% CPU or 10% or ...? – Dustin Boswell Dec 22 '09 at 20:04
  • On an average computer it'll be negligible. – Annabelle Dec 22 '09 at 20:10
  • @Douglas: of course I forgot `Object.watch` in Firefox, built for handling programmatic assignment. – Crescent Fresh Dec 22 '09 at 20:42
  • CF: Why haven't you written an answer yet? ;) Also, getters/setters for DOM properties don't seem to work in Chrome. If you define a setter, Chrome seems to expect a getter too. Also __lookupGetter__() and __lookupSetter__() both return null, so there's no way to actually get/set the real property. – Annabelle Dec 22 '09 at 21:37
  • @Douglas: no answer from me because the result would be too messy. Even I would downvote it. `setInterval` is the "cleanest". Re: `__defineSetter__`, you don't need `__lookupSetter__`/`__lookupGetter__` because there'd be nothing needed from them. Inside the setter implementation you'd just `setAttribute('value', value)`. – Crescent Fresh Dec 23 '09 at 04:11
  • I clearly need to read about getters/setters more. :) Sadly all my work must support IE6, so yeah... :P – Annabelle Dec 23 '09 at 06:58
12

Here is a solution that doesn't make use of jQuery (Its really quite obsolete and not necessary these days)

Using the event "input" you can look for any kind of change:

Deleting, Backspacing, Pasting, Typing, anything that will change the inputs value.

The input event is directly related to the text input. ANY time the text is changed in ANY fashion, input is dispatched.

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>
Mister SirCode
  • 1,575
  • 14
  • 31
  • 1
    @Tylor I offered a simpler solution. jQuery has too many examples of the XY problem. If people want shortcuts, they really shouldn't be programming. Especially when the shortcuts are more performance heavy or resource hungry... Also this answer was from 2019. A little late to be complaining when its been on this network and actively uploaded for almost three years. – Mister SirCode Feb 09 '22 at 11:03
  • 1
    This of course fails to handle the specific scenario that the OP outlined, changes via javascript don't trigger it. – Kevin B Jun 08 '22 at 20:32
6

Here is a slightly different solution if you didn't fancy any of the other answers:

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);

Consider that you cannot rely on click and keyup to capture context menu paste.

Peder
  • 2,739
  • 3
  • 31
  • 33
  • This answer worked best for me. I was having some problems with my selectors till I used: `$("input[id^=Units_]").each(function() {` – robr Nov 04 '12 at 19:25
4

Add this code somewhere, this will do the trick.

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;
};

Found this solution at val() doesn't trigger change() in jQuery

lpradhap
  • 623
  • 6
  • 17
3

I have created a sample. May it will work for you.

var typingTimer;
var doneTypingInterval = 10;
var finaldoneTypingInterval = 500;

var oldData = $("p.content").html();
$('#tyingBox').keydown(function () {
    clearTimeout(typingTimer);
    if ($('#tyingBox').val) {
        typingTimer = setTimeout(function () {
            $("p.content").html('Typing...');
        }, doneTypingInterval);
    }
});

$('#tyingBox').keyup(function () {
    clearTimeout(typingTimer);
    typingTimer = setTimeout(function () {
        $("p.content").html(oldData);
    }, finaldoneTypingInterval);
});


<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>


<textarea id="tyingBox" tabindex="1" placeholder="Enter Message"></textarea>
<p class="content">Text will be replace here and after Stop typing it will get back</p>

http://jsfiddle.net/utbh575s/

Ambuj Khanna
  • 1,131
  • 3
  • 12
  • 32
3

We actually don't need to setup loops for detecting javaScript changes. We already setting up many event listeners to the element we want to detect. just triggering any un harmful event will make the job.

$("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");

and ofc this is only available if you have the full control on javascript changes on your project.

Serdar
  • 91
  • 1
  • 3
3

Although this question was posted 10 years ago, I believe that it still needs some improvements. So here is my solution.

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

The only problem with this solution is, it won't trigger if the value changes from javascript like $('selector').val('some value'). You can fire any event to your selector when you change the value from javascript.

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

Or in a single line

$(selector).val('some value').trigger('change');
hassanrazadev
  • 626
  • 9
  • 15
2

Well, best way is to cover those three bases you listed by yourself. A simple :onblur, :onkeyup, etc won't work for what you want, so just combine them.

KeyUp should cover the first two, and if Javascript is modifying the input box, well I sure hope it's your own javascript, so just add a callback in the function that modifies it.

idrumgood
  • 4,904
  • 19
  • 29
  • Enumerating all the possible events doesn't seem robust to me. Does keyup work if the user holds delete down (i.e. a repeat-key)? What about auto-complete (or toolbars that auto-fill values)? Or are there are special input sources that don't fire keypresses? I'd be worried that there would be something like this that you would miss. – Dustin Boswell Dec 22 '09 at 19:47
1

Here's a working example that I'm using to implement an autocomplete variation the populates a jqueryui selector (list), but I don't want it to function exactly like the jqueryui autocomplete which does a drop-down menu.

$("#tagFilter").on("change keyup paste", function() {
     var filterText = $("#tagFilter").val();
    $("#tags").empty();
    $.getJSON("http://localhost/cgi-bin/tags.php?term=" + filterText,
        function(data) {
            var i;
            for (i = 0; i < data.length; i++) {
                var tag = data[i].value;
                $("#tags").append("<li class=\"tag\">" + tag + "</li>");
            }
        }); 
});
clearlight
  • 12,255
  • 11
  • 57
  • 75
0

Can't you just use <span contenteditable="true" spellcheck="false"> element in place of <input type="text">?

<span> (with contenteditable="true" spellcheck="false" as attributes) distincts by <input> mainly because:

  • It's not styled like an <input>.
  • It doesn't have a value property, but the text is rendered as innerText and makes part of its inner body.
  • It's multiline whereas <input> isn't although you set the attribute multiline="true".

To accomplish the appearance you can, of course, style it in CSS, whereas writing the value as innerText you can get for it an event:

Here's a fiddle.

Unfortunately there's something that doesn't actually work in IE and Edge, which I'm unable to find.

Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31
0

you can simply identify all changers in the form, like this

           //when form change, show aleart
            $("#FormId").change(function () {
                aleart('Done some change on form');
            }); 
Chamod Wijesena
  • 148
  • 3
  • 15
0

You can bind the 'input' event to <input type="text">. This will trigger every time the input changes such as copy, paste, keypress, and so on.

$("#input-id").on("input", function(){
    // Your action
})
Fatih Şennik
  • 1,295
  • 5
  • 12