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:
watch
and observe
polyfills, but they break for an input value propertyWhat 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)
<html>
<body>
<input type='hidden' id='myInput' />
<input type='text' id='myInputVisible' />
<input type='button' value='Test' onclick='return testSet();'/>
<script>
//hidden input which your API will be changing
var myInput=document.getElementById("myInput");
//visible input for the users
var myInputVisible=document.getElementById("myInputVisible");
//property mutation for hidden input
Object.defineProperty(myInput,"value",{
get:function(){
return this.getAttribute("value");
},
set:function(val){
console.log("set");
//update value of myInputVisible on myInput set
myInputVisible.value = val;
// handle value change here
this.setAttribute("value",val);
//fire the event
if ("createEvent" in document) { // Modern browsers
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", true, false);
myInput.dispatchEvent(evt);
}
else { // IE 8 and below
var evt = document.createEventObject();
myInput.fireEvent("onchange", evt);
}
}
});
//listen for visible input changes and update hidden
myInputVisible.onchange = function(e){
myInput.value = myInputVisible.value;
};
//this is whatever custom event handler you wish to use
//it will catch both the programmatic changes (done on myInput directly)
//and user's changes (done on myInputVisible)
myInput.onchange = function(e){
console.log(myInput.value);
};
//test method to demonstrate programmatic changes
function testSet(){
myInput.value=Math.floor((Math.random()*100000)+1);
}
</script>
</body>
</html>
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.