My objective is to observe an input value and trigger a handler when its value gets changed programmatically. I only need it for modern browsers.
I have tried many combinations using defineProperty
and this is my latest iteration:
var myInput=document.getElementById("myInput");
Object.defineProperty(myInput,"value",{
get:function(){
return this.getAttribute("value");
},
set:function(val){
console.log("set");
// handle value change here
this.setAttribute("value",val);
}
});
myInput.value="new value"; // should trigger console.log and handler
This seems to do what I expect, but it feels like a hack as I am overriding the existing value property and playing with the dual status of value
(attribute and property). It also breaks the change
event that doesn't seem to like the modified property.
My other attempts:
- a setTimeout/setInterval loop, but this is not clean either
- various
watch
andobserve
polyfills, but they break for an input value property
What would be a proper way to achieve the same result?
Live demo: http://jsfiddle.net/L7Emx/4/
[Edit] To clarify: My code is watching an input element where other applications can push updates (as a result of ajax calls for example, or as a result of changes in other fields). I have no control on how the other applications push updates, I am just an observer.
[Edit 2] To clarify what I mean by "modern browser", I'd be very happy with a solution that works on IE 11 and Chrome 30.
[Update] Updated demo based on the accepted answer: http://jsfiddle.net/L7Emx/10/
The trick suggested by @mohit-jain is to add a second input for user interaction.
if the only problem with your solution is breaking of change event on value set. thn you can fire that event manually on set. (But this wont monitor set in case a user makes a change to the input via browser -- see edit bellow)
more on firing events manually
EDIT:
The problem with manual event firing and the mutator approach is that the value property won't change when user changes the field value from browser. the work around is to use two fields. one hidden with which we can have programmatic interaction. Another is visible with which user can interact. After this consideration approach is simple enough.
How modern would you like to go? Ecma Script 7 (6 will be made final in December) might contain Object.observe. This would allow you to create native observables. And yes, you can run it! How?
More info: read this.
So yeah, this is highly experimental and not ready in the current set of browsers. Also, it's still not fully ready and not 100% if it's coming to ES7, and the final date for ES7 isn't even set yet. Still, I wanted to let you know for future use.
I wrote the following Gist a little while ago, which allows to listen for custom events cross browser (including IE8+).
Have a look at how I'm listening for
onpropertychange
on IE8.I'm not sure the IE8 solution works cross browser, but you could set a fake
eventlistener
on the propertyvalue
of your input and run a callback once the value ofvalue
changes triggered byonpropertychange
.This is an old question, but with the newish JS Proxy object, triggering an event on a value change is pretty easy:
This creates a JS proxy object based on the original input DOM object. You can interact with the proxy object in the same way you'd interact with the origin DOM object. The difference is that we've overridden the setter. When the 'value' prop is changed, we carry out the normal, expected operation,
obj[prop] = value
, but then if the prop's value is 'value' we fire a custom InputEvent.Note that you can fire whatever kind of event you'd like with
new CustomEvent
ornew Event
. "change" might be more appropriate.Read more on proxies and custom events here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events
Caniuse: https://caniuse.com/#search=proxy
There is a way to do this. There is no DOM event for this, however there is a javascript event that triggers on an object property change.
In the callback you can do whatever.
In this example, we can see this effect (Check the jsFiddle) :
When
obj
change, it activates thewatch
listener.You have more information in this link : http://james.padolsey.com/javascript/monitoring-dom-properties/
The following works everywhere I've tried it, including IE11 (even down to IE9 emulation mode).
It takes your
defineProperty
idea a bit further by finding the object in the input element prototype chain that defines the.value
setter and modifying this setter to trigger an event (I've called itmodified
in the example), while still keeping the old behavior.When you run the snippet below, you can type / paste / whatnot in the text input box, or you can click the button that appends
" more"
to the input element's.value
. In either case, the<span>
's content is synchronously updated.The only thing that's not handled here is an update caused by setting the attribute. You could handle that with a
MutationObserver
if you want, but note that there's not a one-to-one relationship between.value
and thevalue
attribute (the latter is just the default value for the former).