Skip to Content
Technical Articles

HTML5 + JavaScript Pitfalls: When onClick of your Buttons are not fired after onChange of your Inputs

I started to answer the question of Mike Doyle, but then I realized my answer became a little too long. So I decided to write this little blog instead.

As Mike mentioned, this issue is not new (follow my code below or read Mike’s question). I’ve had this issue quite often in UI5 projects, and the workarounds are just workarounds which I would prefer to avoid, i.e.

  • doing it the way Mike does it (if it’s possible)
  • working with setTimeout (if possible, may need additional boilerplate code)
  • switching to liveChange if possible (may need additional boilerplate code)
  • moving all the code from event handlers into onPress of button if possible (may need additional boilerplate code)
  • executing only code in the change handle which does not block the press event (i.e. avoid alert(), avoid certain other operations changing the DOM)

I can’t give a rule of thumb because ​I’ve used different approaches based on the given scenarios.

See the following native HTML5/JS examples (or ​this jsbin for live demos), which I have tested on Safari 12.1.2, Chrome 76, Firefox 68.0.2, IE 11:

 

1. Works as expected: two events are always fired in correct order

<input onchange="console.log('onchange')">
<button type="button" onclick="console.log('onclick');">Click me</button>

This is what we expect in general – when changing the value of the input field and clicking the button we want to see the “onchange” before “onclick” in the console output. This works just fine.

 

2. Works as expected: two events are always fired in correct order

<input onchange="console.log('onchange');this.nextElementSibling.style.color='green';">
<div>Make me green in 'onChange'</div>
<button type="button" onclick="console.log('onclick');">Click me</button>

Even this works just fine in all browsers I’ve tested. First, we see “onchange” in the console, then the color of the text is changed to green via CSS, and finally we see “onclick” in the console. So even this small DOM change (the CSS change) is not causing any issues.

 

3. Attention: only onchange is fired on very first click (all browsers)

<input onchange="console.log('onchange');this.nextElementSibling.style.display='none';">
<div>Hide me in 'onChange'</div>
<button type="button" onclick="console.log('onclick');">Click me</button>

So let’s do another DOM change, but this time some change which is slightly “bigger” – something that hides a DOM element. Interestingly, in this case only “onchange” will be printed in your console the first time consistently across all browsers. When you change the input field’s value again and press the button again afterwards, the everything works as expected (because the <div> is already hidded).

 

4. Attention: only onchange is fired in Chrome/IE11/Safari

<input onchange="console.log('onchange');alert('onchange')">
<div>Show alert() in 'onChange'</div>
<button type="button" onclick="console.log('onclick');">Click me</button>

When using an alert() in onchange it gets weird. While Firefox shows “onclick” in the console after the alert() is closed by the user, the other browsers for some reason swallow the “onclick” event (meaning onclick is not triggered). The reason may be that onchange is triggered when the focus of the input field is lost, and when the dialog is opened and the user presses “ok” there can’t be an onclick on the button (there’s not even a focus on the button) – I guess this is why Chome/IE11/Safari behave like that. However, I kinda prefer the Firefox behavior here, it feels more natural to me.

Hint: alerts are considered evil theses days by the way.

 

5. Attention: onchange is fired AFTER onmousedown (all browsers)

<input onchange="console.log('onchange');alert('onchange')">
<div>Show alert() in 'onChange'</div>
<button type="button" onmousedown="console.log('onmousedown');">Mouse Down</button>

Ok, then maybe we could use ommousedown here. Well, we could, and in fact it works in all browser I’ve tested. However, the change event of the input field is fired after the mousedown event of the button. So now the order of the events is a little different. This is something I have to be aware of.

 

6. Attention: only onchange is fired, onmouseup is not fired at all in Chrome/Safari

<input onchange="console.log('onchange');alert('onchange')">
<div>Show alert() in 'onChange'</div>
<button type="button" onmouseup="console.log('onmouseup');">Mouse Up</button>

Well, then let’s try onmouseup. Interestingly, onmouseup fires as expected and in the right order in Firefox and IE11. However, both Chrome and Safari don’t fire onmouseup at all. IMHO, this feels strange.

You could also play around with oninput, I leave this as a task for you…

 

7. Conclusion

Well, this doesn’t make life easier (as a JavaScript/WebDeveloper in general):

  • we have different browsers behaving deifferently
  • this is not a bug in UI5 itself, it’s more about the browser’s implementation
  • this issue is not even new, it’s been around for mayn years
  • not sure why browser vendors aren’t trying to tackle that flaw

All you can do is

  • making sure to test your code on different browsers
  • create awareness by telling other (ui5) developers
  • if possible, try to avoid code in onchange which changes the DOM (and I confess I can’t avoid it myself in too many cases. In such cases good luck. 🙂
  • find some workaround based on your specific case

 

I know this is not a good conclusion. I’d be happy to hear a better conclusion from anyone else. This blog is more for creating some awareness.

1 Comment
You must be Logged on to comment or reply to a post.
  • Thanks so much for taking the time to explain this, Nabi Zamani. It makes sense now that you say the trigger is the first (change) event making a change to the DOM.  That could be showing an alert, or in our case (in our real UI5 app) making a service call and setting the busy indicator.

    Even setting a breakpoint in the change event handler is enough to stop it working correctly.  That is why I thought the alert in the test wasn’t the cause, because I still didn’t hit my breakpoints after I removed the alert.

    I’ve done some more tests with a UI5 app and I can confirm that it does work correctly in Chrome (as long as you don’t make any DOM changes).

    I also tried putting the alert back in, but putting it in a timeout.  If the timeout was >= 200ms then it worked as desired, i.e. we got

    1. Change event
    2. Submit event
    3. Alert from change event

    I was surprised to see that a timeout of 100ms did not do the trick.

    Now I’m pondering which is the lesser of two evils.  Is it the mousedown instead of click?  Or is it a timeout before making our service call?