I'm writing a user script which needs to respond to an <input>
element's value changing, no matter how the change was made.
For user interactions, this is easy — just add a listener for the input
event. However, I haven't yet found a way to respond to the input's value being set directly by changing its .value
property.
I've tried adding listeners for the change
and input
events (neither are triggered) and adding a MutationObserver
for both the input's attributes and the input's parent's child list (again, neither are triggered). There also doesn't appear to be a property descriptor I can modify.
How can I trigger code to run when an <input>
element's .value
property is set?
<!DOCTYPE html>
<html>
<head>
<title>Detect value change</title>
<script type="module">
const range = document.querySelector('input');
range.addEventListener('change', (event) => console.log('Change:', event));
range.addEventListener('input', (event) => console.log('Input:', event));
const observer = new MutationObserver((mutations) => console.log('Mutation:', mutations));
observer.observe(range, { attributes: true, attributeFilter: ['value'] });
observer.observe(range.parentNode, { childList: true });
console.log('Property descriptors:', Object.getOwnPropertyDescriptors(range));
// Somewhere else, in code I don't control:
document.querySelector('input').value = 7;
</script>
</head>
<body>
<input type="range" min="0" max="10" />
</body>
</html>
Console output:
Property descriptors: {}
CodePudding user response:
Fire the change event after updating the value programmatically:
range.value = 7;
var event = new Event('change');
range.dispatchEvent(event);
CodePudding user response:
See this answer for great information on why MutationObserver
will not catch modifications to properties.
If you are unable to modify the way the value is being changed (node.setAttribute('value', 'newValue')
would fire your MutationObserver
, for example), then I think your only option is unfortunately a setInterval
that poles for value changes at a set frequency.
Something like this:
const range = document.querySelector('input');
document.getElementById('incrementValue').addEventListener('click', () => {
const val = range.valueAsNumber;
range.value = range.value === range.max ? range.min : String(val 1);
});
let currentVal;
setInterval(() => {
// Check for val changes every 1sec
if (range.value === currentVal) {
return;
}
currentVal = range.value;
console.log(`New value detected. New value is ${range.value}`);
}, 1000);
<!DOCTYPE html>
<html>
<head>
<title>Detect value change</title>
</head>
<body>
<button id="incrementValue">Increment value</button>
<input type="range" min="0" max="10" value="0" />
</body>
</html>