6

Environment:
Only JavaScript (no jquery :( )
Button click event handler is not available for change.

Is there a way to listen to textbox if its value is changed programmatically?

<input type="button" id="button1" value="button" />
<input type="text" id="text1" />


var btn1 = document.getElementById('button1');
var txt1 = document.getElementById('text1');
btn1.onclick=function(){ txt1.value = 'hello world'}

https://jsfiddle.net/yrt7e57w/

lewis4u
  • 14,256
  • 18
  • 107
  • 148
Rod
  • 14,529
  • 31
  • 118
  • 230

3 Answers3

4

You can use element.dispatchEvent(event) when you change the text programmatically. In your example it would look like:

var btn1 = document.getElementById('button1');
var txt1 = document.getElementById('text1');

btn1.addEventListener("click", function() { // Add to OnClick of button.
    txt1.dispatchEvent(new Event('change')); // force change event to run on textbox.
});

txt1.addEventListener("change", function(e){ // EventListener for OnChange of the element.
    alert("changed");
});

Just the addEventListener on its own will not work as it isn't exactly being changed in the DOM.

Above is a simple solution which requires a few key presses but for a much more maintainable and reusable solution, check out Snowmonkey's answer (and probably most other answers to this question), https://stackoverflow.com/a/44708746/640263.

Peter Dempsey
  • 485
  • 1
  • 9
  • 17
  • Any way for it to work outside button click handler (that part is sealed). I apologize for failing to mention that. – Rod Jun 22 '17 at 20:16
  • I've updated my answer, you should still be able to add `click` events to an element via `addEventListener`. – Peter Dempsey Jun 22 '17 at 20:25
  • In my view, the text element itself should be responsible for triggering that onchange -- wiring the connection between the button and the text box is adding a hard dependency that may not be flexible enough. – Snowmonkey Jun 22 '17 at 20:28
  • @Snowmonkey I agree with you if you aren't using any sort of binding framework, it keeps it simple and easier to find what does what but it is always dependent on what needs to be done, so in this case, I think it is the best solution. – Peter Dempsey Jun 22 '17 at 20:32
  • Take a look at mine -- the text box itself is listening for changes, using the Object.set, and triggering the onChange on itself. Thus it doesn't care what triggers the change, doesn't care what other elements are on the page, it simply takes care of its own business. ;) – Snowmonkey Jun 22 '17 at 20:33
3

You can rely on the coder programmatically changing the element to know to trigger the onChange, but that's an iffy proposition. Looking through other posts, this looks very promising: for your text element, override the setters and getters, so that they automagically trigger for either keyed changes or programattic ones.

var btn1 = document.getElementById('button1');
var txt1 = document.getElementById('text1');


btn1.onclick = function() {
    txt1.value = 'hello world'
  }
  
txt1.addEventListener("change", function(e){
  console.log(txt1.value);
})
  
  //property mutation for hidden input
Object.defineProperty(txt1, "value", {
  // Don't override the getter, but stub it in.
  get: function() {
    return this.getAttribute("value");
  },
  // In the setter, we want to set the value
  //  and also fire off the change event.
  //  By doing this, the coder changing the
  //  value never needs worry about it.
  set: function(val) {
    console.log("set");

    // handle value change here
    this.setAttribute("value", val);

    //fire the event
    if ("createEvent" in document) { //NON IE browsers
      var evt = document.createEvent("HTMLEvents");
      evt.initEvent("change", false, true);
      txt1.dispatchEvent(evt);
    } else { //IE
      var evt = document.createEventObject();
      txt1.fireEvent("onchange", evt);
    }
  }
});
<input type="button" id="button1" value="button" />
<input type="text" id="text1" />

Or see it as a fiddle Here

So, to answer your question about why the click handler is showing the input as having a null value, it's because the getter/setter are overriding the default value behavior. The easiest way to work around this is to create a custom getter/setter to act as an interface to the value attribute:

var btn1 = document.getElementById('button1');
var txt1 = document.getElementById('text1');

btn1.onclick = function() {
  console.log("in the button's click handler, ");
  console.log("Value is: " + txt1.val);
  console.log("--------------------------------")
  txt1.val = 'hello world'
}

txt1.addEventListener("change", function(e) {
  console.log("in txt1.onChange function...")
  console.log(this.val);
  console.log("--------------------------------")
})


//property mutation for hidden input
Object.defineProperty(txt1, "val", {
  get: function() {
    return this.value;
  },
  set: function(val) {
    // handle value change here
    this.value = val;

    //fire the event
    if ("createEvent" in document) { //NON IE browsers
      var evt = document.createEvent("HTMLEvents");
      evt.initEvent("change", false, true);
      txt1.dispatchEvent(evt);
    } else { //IE
      var evt = document.createEventObject();
      txt1.fireEvent("onchange", evt);
    }
  }
});
<input type="button" id="button1" value="button" />
<input type="text" id="text1" />

What's happening here is, when the button is clicked, I get the val attribute of the input (which gets the value behind the scenes), then programmatically sets the val attribute of the same input (which, again, sets the value attribute). For some reason, you can't use get/set in object.defineProperty on an input's value attribute without completely breaking it. So in the console, you'll see THREE function calls: when you click the button, the input loses focus, triggering its change method, but then the button itself triggers its click handler, which then changes the value of the input and triggers the change handler one more time.

Hope this helps!

Again, to see this as a fiddle...

Snowmonkey
  • 3,716
  • 1
  • 16
  • 16
  • Bonus: if I type something in the text and try to alert it for first time it shows null, can you possibly shed some insight as to why? https://jsfiddle.net/dyLb6543/1/ – Rod Jun 23 '17 at 18:44
  • 1
    @Rod, take a look at the changes to my post. It's a little kludgy, as it creates a custom val attribute to function as an interface to the value attribute, but it works quite nicely. – Snowmonkey Jun 29 '17 at 20:23
0

Maybe this is overkill,
but you could use the mutationObserver api to listen to an attribute change,
you would then have to change the value through setAttribute method:

var btn1 = document.getElementById('button1');
var txt1 = document.getElementById('text1');

btn1.onclick=function(){ txt1.setAttribute('value', 'hello world') }

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log( mutation );
  });    
});
 
// configuration of the observer:
var config = {  attributes:true };
 
// pass in the target node, as well as the observer options
observer.observe(txt1, config);
<input type="button" id="button1" value="button" />
<input type="text" id="text1" />
maioman
  • 18,154
  • 4
  • 36
  • 42