zero_cool
zero_cool

Reputation: 4254

How do you listen / detect changes to an input value - when the input value is changed via JavaScript?

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

Answers (6)

Daniel Black
Daniel Black

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

Péter Magyar
Péter Magyar

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

Nom
Nom

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

rolinger
rolinger

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

Khaledonian
Khaledonian

Reputation: 2203

Consider creating and triggering input events

var event = new Event('input', {
    bubbles: true,
    cancelable: true,
});

then

myelement.dispatchEvent(event);

more info

Upvotes: 2

CertainPerformance
CertainPerformance

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

Related Questions