Search code examples
javascripthtmlinputpropertiesobservers

native HTMLElement properties observer


Hope you are all doing well! I Have a small question that maybe a lot of you have already thought about... Is there any solution to listen for native HTMLElement properties (not attributes) updates? I explain:

<input type="text" value="hello" />

I would like to be notified when something in the codebase does this:

myInput.value = 'world';

I can know that the attribute itself has been updated with MutationObserver or attributeChangedCallback function but not when the codebase assign the value through the property directly...

I have tried to do something like this:

Object.defineProperty(myInput, 'value', {
   set : (newValue) => {
      console.log('value property updated');
      // I can't do something like this.value = newValue
      // cause it will trigger an infinite loop...
   }
});

The issue with that is that now the default behavior of the myInput.value = 'world'; does not work anymore and the value is not actually changed inside the field...

I would like to apply this concept to others properties as well like "min", "max", "placeholder", etc...

In summary, I just want to observe some properties without changing any of their default behaviors...

Any idea?

Thanks in advance guys!

Cheers!


Solution

  • You need to get native property descriptor first. You can get one from element's prototype.

    const nativeValueDesc = Object.getOwnPropertyDescriptor(input.constructor.prototype, 'value');
    

    Then you can use it in your setter and getter to reflect native behavior

    Object.defineProperty(input,'value',{
      set(val){
        console.log('value property updated', val);
        // Continue with native behavior
        return nativeValueDesc.set.call(this, val);
      }
      /* ... */
    });
    

    Live example at http://jsbin.com/juqili/6/edit?html,js,console,output

    To be able to observe already observed element, or just an element with already provided own descriptor, you can do

    const nativeValueDesc = Object.getOwnPropertyDescriptor(input, 'value') || Object.getOwnPropertyDescriptor(input.constructor.prototype, 'value');