Detect HTMLobject changes caused by JS

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?

Answer

  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.

Leave a Reply

Your email address will not be published. Required fields are marked *