4

Suppose I have a select element <select id='test'> and an onchange EventListener attached to it. This EventListener detects all changes caused by user: both MouseEvents and KeyboardEvents.

But, when I change the value from code:

document.getElementByID('test').value = 'new value';

ChangeEvent does not fire.


My questions are:

  1. What ChangeEvent is looking at, when it fires? / How does it work "from the inside"?
  2. Am I able to detect changes, that have fired from JS code without intervals?
  3. As I have figured out the ChangeEvent does not fire natively when the value is being updated from code, because Event object is not created. Why it was designed like this?
Andrew Evt
  • 3,613
  • 1
  • 19
  • 35
  • 2
    Possible duplicate of [Detect all changes to a (immediately) using JQuery](http://stackoverflow.com/questions/1948332/detect-all-changes-to-a-input-type-text-immediately-using-jquery) which shows the only way to catch __changes coming from JS__ is intervals. – blex Mar 18 '16 at 13:48
  • 1
    there are no JS changes situations in duplicate question, read it first before marking! – Andrew Evt Mar 18 '16 at 13:51
  • 1
    I did read correctly. You should read the accepted answer of the duplicate question, and comments on the highest scored question. – blex Mar 18 '16 at 13:52
  • yes, it uses intervals, as you can see. But I'm looking for a better way, and maybe someone will tell how `change` event works – Andrew Evt Mar 18 '16 at 13:55
  • 2
    Have you looked into [MutationObservers](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)? Limited support, but they track changes without intervals. – colonelsanders Mar 18 '16 at 13:56
  • @colonelsanders thx! will read about it! It seems like it is exactly what I am looking for) – Andrew Evt Mar 18 '16 at 14:02
  • 1
    Mutation observer is not the best solution for it, especially you have a lot elements you need to observe. If this change mede but you, and you need event be fired, just trigger it. – ilyabasiuk Mar 18 '16 at 14:14
  • Why not [fire a custom event](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent) from the code that changed the element? – NullDev Mar 08 '21 at 08:44
  • 1
    MutationObservers only check for changes in the DOM. Changing the value *property* does not cause the DOM to mutate, thus it cannot be observed with MutationObservers. – connexo Mar 08 '21 at 11:30

1 Answers1

3
  1. What ChangeEvent is looking at, when it fires? / How does it work "from the inside"?

The change Event (note that there is no ChangeEvent interface) just like any event, is not looking at anything. Events are fired by various algorithms as part of these algorithms directly.

For an input with a "text" type, the algorithm that fires the change event is here.

It currently reads

For input elements without a defined input activation behavior, but to which these events apply, and for which the user interface involves both interactive manipulation and an explicit commit action, then when the user changes the element's value, the user agent must queue an element task on the user interaction task source given the input element to fire an event named input at the input element, with the bubbles and composed attributes initialized to true, and any time the user commits the change, the user agent must queue an element task on the user interaction task source given the input element to fire an event named change at the input element, with the bubbles attribute initialized to true.

Some other types of input do have different algorithms, defined by their input activation behavior.


  1. Am I able to detect changes, that have fired from JS code without intervals?

Sure, your code made these changes, you can certainly hook a callback where it did so.
Now, for debugging purposes, you could overwrite your input's value property so that it calls your callback whenever the value is set:

{
  const inp = document.querySelector("input");
  const original_desc = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, "value");
  const new_desc = Object.assign( {}, original_desc, {
    set(val) {
      console.log( "value just changed programmatically:", val );
      return original_desc.set.call( this, val );
    }
  } );
  Object.defineProperty( inp, "value", new_desc );
}

document.querySelector("button").onclick = () => {
  document.querySelector("input").value = "foo";
};
<input>
<button>change value programmatically</button>

But really, that you have to resort to this kind of hack means you have a bigger design issue you should fix.


  1. As I have figured out the ChangeEvent does not fire natively when the value is being updated from code, because Event object is not created. Why it was designed like this?

Because the change event, like the input one "are fired to indicate that the user has interacted with the control." source

It is expected that as a web-author, you stay in control of the scripts that do run on your page, so it makes no sense to fire an event for something your script did on its own, it should be able to fire this event itself.

Kaiido
  • 123,334
  • 13
  • 219
  • 285