Reputation: 4254
I've got an input
with Google AutoComplete connected.
When the user moves up and down through the seachResults
the value of the input is changed dynamically via JavaScript.
I'd like to capture the value that is in the input at any given time.
I've tried onChange
and onInput
but since no event is getting fired, and the value
of the DOM Node is not getting set - there are no updates.
How do you detect changes to the input
that are dynamically updated via JavaScript?
Upvotes: 16
Views: 7019
Reputation: 605
I liked the idea of overriding the setter method to allow for callbacks, but the answers provided only work if you need to add a single 'event listener' to the element. They fail if multiple are added since each previous callback overrides the previous. Here is the solution I came up with to allow for any number of callbacks to be added:
function addProgramaticChangeListener(selector, callback) {
const input = document.querySelector(selector);
if (input) {
const desc = Object.getOwnPropertyDescriptor(input, "value")
? Object.getOwnPropertyDescriptor(input, "value")
: Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
if(input.setterCallbacks && input.setterCallbacks.length){
input.setterCallbacks.push(callback)
}else{
input.setterCallbacks = [callback]
Object.defineProperty(input, "value", {
configurable: true,
get: desc.get,
set: function (v) {
for(var callback of input.setterCallbacks){
callback(v)
}
desc.set.call(this, v);
}
});
}
}
}
Each callback is added to the element and referenced in the setter. Whenever the input is changed, all callbacks will be fired with the new value passed.
Upvotes: 0
Reputation: 71
You can start events manually with JS.
<script>
const my_input = document.getElementById("my_input");
my_input.onchange=function(){console.log("changed!");};
const event = new Event('change');
my_input.dispatchEvent(event);//console writes "changed!" now.
//"event" can be reused like:
//my_input2.dispatchEvent(event);
//my_input3.dispatchEvent(event);
</script>
Upvotes: 1
Reputation: 373
You can run this after programmatically changing the .value
document.querySelector('#element-id').dispatchEvent(new Event('input'));
Listen for a value change:
document.querySelector('#element-id').addEventListener('input', function() {
console.log(this.value);
});
Upvotes: 2
Reputation: 3060
Other solutions above require listening to all inputs or all of the DOM, but here is a different solution designed to detect a value change of a specific element:
You can use a setInterval. I needed to observe a specific DOM element value changing and I was able to target the specific elementID with the following:
var watchID ;
var el = document.getElementById('elementID') ;
var oldValue = el.value ;
var watchCount = 0 ; // create a built in timeout function
watchID = setInterval(function() {
watchCount++ ;
if (el.value != oldValue) {
console.log("Element Changed:" +el.value) ;
... do something ...
clearInterval(watchID) ;
}
// 30 second timeout
if (watchCount == 60) {
console.log("Interval Timeout")
clearInterval(watchID) ;
}
},500) ; // execute every 1/2 second for 30 seconds
In my case, I had a complex file download that needed a bit of a different hacky solution and the above worked without any eventListeners or other more complex solutions that monitored everything (like: MutationObserver
). Of course you don't need the custom timeout...but if you don't, you may want a more friendly solution than mine. A continuous interval could strain resources.
Upvotes: 0
Reputation: 2203
Consider creating and triggering input events
var event = new Event('input', {
bubbles: true,
cancelable: true,
});
then
myelement.dispatchEvent(event);
Upvotes: 2
Reputation: 370599
The .value
attribute will only change if the script-writer has called setAttribute('value'
to set the new value, which is quite an odd thing to do. In almost all situations, I would expect the value to be set by assigning to the value
property directly, eg:
input.value = 'foo';
Calling setAttribute
will indeed show a change in the inspected DOM attribute, eg:
<input value="somevalue">
const input = document.querySelector('input');
input.setAttribute('value', 'foo');
console.log(input.outerHTML);
<input>
But just assigning to the .value
property of the element will not result in such a change:
const input = document.querySelector('input');
input.value = 'foo';
console.log(input.outerHTML);
<input>
Assigning to the .value
actually invokes a setter function on HTMLInputElement.prototype
:
console.log(HTMLInputElement.prototype.hasOwnProperty('value'));
You can shadow this by putting a getter/setter for value
directly on the element itself, with the setter invoking your own function, allowing you to listen for changes:
const { get, set } = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
const input = document.querySelector('input');
Object.defineProperty(input, 'value', {
get() {
return get.call(this);
},
set(newVal) {
console.log('New value assigned to input: ' + newVal);
return set.call(this, newVal);
}
});
// example of autocomplete trying to change the input value:
input.value = 'foo';
<input>
Upvotes: 21