Overriding Inline onclick Attributes With Event Capturing

Published: February 16, 2017

Tags:

Recently, I needed to override a <button>’s inline onclick attribute. I was writing a plugin for Magento, which, for better or worse, makes heavy use on inline onclick attributes. If you run a Google search you’ll see that the canonical answer looks something like this…

document.getElementById('my-id').onclick = myOnClick;

For most uses cases this work fine. However, there is a caveat that should accompany this answer. It doesn’t work for elements that are dynamically added to the document.

But never fear, there’s another approach that can be used to override inline onclick attributes that works with dynamically added elements. And that approach, my friends, is called event capturing.

Some Background

It is worthwhile to start out with some background information about how event propagation works in JavaScript. Below is a diagram from w3.org which offers a good visual explanation.

A diagram visually showing how event capturing and bubbling works

In all modern browsers, each click event goes through two distinct phases.

Event capturing

The event descends the document tree starting with the top most element (document) down to the target. The event can be captured at any step along the way, before it reaches the target. This can be done by setting the useCapture flag to true when registering an event listener…

 element.addEventListener('click', (e) => {
   console.log('Hey, there');
 }, true)
Event Bubbling

After the event reaches the target it bubbles back up through all parents all the way to the top (again, document). If you register an event listener on the target’s parent without setting useCapture it will be called after any event listener bound to the target as a result of bubbling.

How We Can Use Event Capturing To Achieve The Desired Effect?

We can take advantage of event capturing to override inline onclick attributes by capturing the event in a parent element and stopping further propagation. Doing so looks something like this.

<button class="button" onclick="window.location='https://www.google.com'">Hello</button>
<script>
  document.addEventListener('click', (e) => {
    if (e.target.classList.contains('button')) {
      e.stopPropagation();
      console.log('gotcha');
    }
  }, true);
</script>

A Note On jQuery

It is worth noting that jQuery provides no means for setting the useCapture flag on an event listener. In order to use this technique you need you use the vanilla JS’s addEventListener.

Conclusion

I hope some of you found this write up helpful. If you have any questions or comments, feel free to drop a note below, or, as always, you can reach me on Twitter as well.

Max Chadwick Hi, I'm Max!

I'm a software developer who mainly works in PHP, but loves dabbling in other languages like Go and Ruby. Technical topics that interest me are monitoring, security and performance. I'm also a stickler for good documentation and clear technical writing.

During the day I lead a team of developers and solve challenging technical problems at Rightpoint where I mainly work with the Magento platform. I've also spoken at a number of events.

In my spare time I blog about tech, work on open source and participate in bug bounty programs.

If you'd like to get in contact, you can find me on Twitter and LinkedIn.