46

Is there a way to get informed when a script changes the value of an input type text.

I have

<input id='test' type='text' />

and a script

document.getElementById('test').value = 'bbb'; 

I want to be notified of the change of the text.

I know I could be notified by keydown,keyup, onblur etcetera (which works if I am trying to track user typing) but what about if the change is done programmatically ?

Many thanks

p.s. No JQuery please or if jquery does it can somebody explain how it achieves it.

Zo72
  • 14,593
  • 17
  • 71
  • 103
  • in the script where it calls document.getElementById('test').value = 'bbb'; y dont you call a function – 999k Apr 15 '13 at 10:43
  • @555k what is it that I have to register for ? For example if I were typing document.getElementById('test').onkeyup = function () { ... } that would not work – Zo72 Apr 15 '13 at 10:48
  • An idea would be to "inherit" from the DOMElement, define a "value"-property with getter and setter (as @MaxArt proposed) and inside the getter and setter use the original "value"-attribute of the DOMElement. – Johannes Egger Apr 15 '13 at 11:23
  • @Zo72 what did you do with this question what did you settle for – codefreaK Jun 16 '16 at 21:39
  • @kaiido I don't understand why you closed this question as a duplicate of that other one. If anything, it should be the other way around. – Félix Adriyel Gagnon-Grenier Sep 29 '22 at 19:06

3 Answers3

29

I find the simplest way is to actually trigger the event manually:

document.getElementById('test').value = 'bbb';
var evt = new CustomEvent('change');
document.getElementById('test').dispatchEvent(evt);

Just listening to the change event is error-prone when the value is changed programmatically. But since you are changing the value, add two lines and fire the change event, with a CustomEvent.

then you'll be able to catch this change event, either inline:

<input id="test" onchange="console.log(this)">

or with a listener:

document.getElementById('test').addEventListener('change',function(event) {
    console.log(event.target);
});

this have the advantage that if you have an input which can be changed by the user, you can catch both those events (from the user or the script)

this however depends on the fact that you are yourself programmatically changing the value of the input. If you are using libraries (such as datepickr) that change the values of your inputs you may have to get in the code and add those two lines at the right place.

Live Demo:

// input node reference
const inputElem = document.querySelector('input')

// bind "change" event listener
inputElem.addEventListener('change', e => console.log(e.target.value))

// programatically change the input's value every 1 second
setInterval(() => { 
  // generate random value
  inputElem.value = Math.random() * 100 | 0; 
  
  // simulate the "change" event
  var evt = new CustomEvent('change')
  inputElem.dispatchEvent(evt)
}, 1000);
<input>
vsync
  • 118,978
  • 58
  • 307
  • 400
  • This souldn't work, so what am I missing? If the user opens the console and does `$0.value = "asd"` (see [$0](https://developers.google.com/web/tools/chrome-devtools/console/utilities#dom)), how does this capture `change` then? Do we ask, kindly, the user to trigger the change event? :) – akinuri Nov 02 '19 at 11:42
  • @akinuri Hmmm, I think I am missing something too from your comment :) My answer is intended to address the use case of triggering the change event on programmatic, that is, not by a user opening the developper console, but really when javascript runs and changes an input's value, so it might not work with that the $0 thing, I am not too aware of it. Other than that, it most definitely works, why shouldn't it? – Félix Adriyel Gagnon-Grenier Nov 03 '19 at 23:14
  • What I meant is, the OP is talking about the "user", not himself/herself (as the developer). If s/he's making the change programmatically in his/her code, then fine, there are workarounds to capture the change, but if we're talking about the (end-) user, I doubt that there's a foolproof solution. – akinuri Nov 04 '19 at 06:37
  • 1
    @akinuri I believe you are slightly mistaken. The user is mentioned, in the context of typing in a normal text input: "`I know I could be notified by keydown,keyup, onblur etcetera (which works if I am trying to track user typing) but what about if the change is done programmatically ?`". those events are normal events being fired when interacting with text inputs. The accepted answer also sees that as user interacting with normal inputs. Both the accepted answer and mine cover the end-user typing in inputs, as well as programmatic changes. – Félix Adriyel Gagnon-Grenier Nov 04 '19 at 13:14
  • ... and actually, the first sentence of the question really removes any confusion possible, we are definitely **not** talking about using the console: `"Is there a way to get informed when a script changes the value of an input type text."` OP knows how to handle events thrown with normally tying into inputs, and they want to add programmatic event handling to that. (errata, accepted answer only covers programmatic changes, whereas I cover both) – Félix Adriyel Gagnon-Grenier Nov 04 '19 at 13:18
  • Hmm, I guess I have a different definition for "programmatic change". If a user uses keyboard to fill a box, I'd call that manual change. If a user tries to change the box value through commands (such as `inputBox.value = "asd"`), I'd call that programmatic change. While there are event listeners for manual changes (key events), there isn't a listener for "programmatic changes". For example, I might have a userscript manager (extension) installed on my browser. With that, I can change the input values in a page (e.g. autofill forms). I'd call this programmatic change. – akinuri Nov 04 '19 at 13:51
  • What if the developer of the page that I'm autofilling forms wants to detect these autofills? S/he can't using this approach. Detecting should be at a lower level, and probably `inputBox.value` needs to be directly monitored... Btw, just to be clear, I'm not trying to argue :) It's just that this solution isn't applicable to the scenario in my mind (a user tinkering with/trying to hack the input boxes). – akinuri Nov 04 '19 at 13:51
  • @akinuri I think I understand you, that's what I mean by programmatically as well, but "opening the console and writing in it" is not part of the normal workflow of using a webpage, for non-programmers. What I am proposing, what I use to solve this problem, is that when you write code that changes these value (such as a usercript) that you trigger the change event yourself, right after the `inputBox.value = "asd"`. Normal listeners can then act on it. – Félix Adriyel Gagnon-Grenier Nov 04 '19 at 14:40
  • 1
    I think the place where we differ, is that I presume the programmatic change to originate from the programmer writing the script, whereas you consider the programmatic change to originate from the user consulting a webpage. – Félix Adriyel Gagnon-Grenier Nov 04 '19 at 14:42
  • 1
    This is the best answer! I have been looking for this for several months already! – Jones G Jan 09 '21 at 13:41
  • 3
    The problem is when you want to detect value changes in code **you are not the author of**, so you cannot simply fire a fake event whenever the value changes, but must *detect* it somehow. (I have a 3rd-party plugin which needs to detect such thing) – vsync Sep 18 '21 at 22:13
25

If you're dealing with a modern browser, you can try with something like this:

var input = document.getElementById('test');
input._value = input.value;
Object.defineProperty(input, "value", {
    get: function() {return this._value;},
    set: function(v) {
        // Do your stuff
        this._value = v;
    }
});

This solution is good if you actually don't expect any user input (i.e., hidden type input fields), because it's extremely destructive of the DOM basic functionality. Keep that in mind.

MaxArt
  • 22,200
  • 10
  • 82
  • 81
  • 3
    It seems that when you redefine the `value`-attribute the input field is no longer "connected" to the `value`-attribute. – Johannes Egger Apr 15 '13 at 11:06
  • 2
    @MaxArt that's really clever... but are there any side effects ? I am a bit worried about redefining the 'value' property – Zo72 Apr 15 '13 at 11:19
  • 1
    @Zo72 The side effects it's mainly the one that Jasd mentioned, plus the problem that if the user types a new value there's *no way* you can detect what he typed, unless you're going to capture every single key event and interpret it. That's a mess already, but if you add cut/copy/paste events, it's gonna get huge. – MaxArt Apr 15 '13 at 11:49
  • @MaxArt I like it a lot. I just wish somebody had done it already and there was a github snippet. It seems quite low level stuff. I will do it and test it and if it works I will mark best answer – Zo72 Apr 15 '13 at 13:06
  • Thank you! It works like a charm. I use this only to determine which script changes the input value. `set: function(v) { console.log('input value changed to',v); console.trace(); this._value = v; }` – spiilmusic Aug 10 '17 at 09:18
4

A less destructive version of MaxArt's solution is to call the actual HTMLInputElement's setter and getter afterwards:

const input = document.querySelector("input");
const desc = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
Object.defineProperty(input, "value", {
    get: desc.get,
    set: function(v) {
        console.log("setting programmatically", v);
        desc.set.call(this, v);
    }
});

input.value = "This is correctly set everywhere";
const fd = new FormData(input.parentNode);
console.log("It gets sent as part of the form", ...fd);

input.oninput = (evt) => {
  console.log("user inputs still do work");
};
<form>
  <input name=inp>
</form>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • I really like this solution. I would only add (to both this one and MaxArt's answer) that, in case of radio or checkbox inputs, it doesn't work because the property being changed is "checked". This can be fixed using "checked" instead of "value" for these input types. – Alobar Sep 21 '22 at 16:12