Learning To Listen, All About JavaScript Event Listeners

Romario Fitzgerald
6 min readAug 2, 2019
Photo by Alireza Attari on Unsplash

Hey all, I’m going to be honest, I thought I knew JavaScript (JS), but then my boss threw me headway into a particular project. Using jQuery as a base, I was supposed to build a thin framework for a content management system. This was some years ago, and trust me when I say, the javascript battle was hard fought. And I learnt a lot, so today I want to share it with you.

I ran into many issues when developing this application, one of the most annoying was with how different browsers handled the JS. Thankfully, today I know better. What to do and what not to do, so testing in various browsers becomes easier, because you’re not having to run into a bunch of errors and having to go fix and test all over again.

JavaScript Event Listeners are fairly simple enough, when dealing with static page content. But we were handling dynamically loaded DOM elements, multiple layers at that. So that threw some complications into things, the regular “element.addEventListener” wasn’t going to cut it.

Let’s go through my journey and look at a few ways we can tackle these event listeners and discuss what works when and why it works.

Let’s Start With Plain JavaScript

elem = document.querySelector('#my_button')
elem.addEventListener('click', function(){
console.log('clicked')
}, false)

This method is probably the simplest, we get our element by some attribute, then we call our “addEventListener” method on it.

The arguments are as follows:

  1. The first argument we pass is our action that we want to track with our event listener
  2. The second aregument we pass is our function we want to execute as a result of our event
  3. Boolean, whether we want the event the continue forward or not, that is, whether or not we want any other actions to take place after this event listener is finished.

You’ll see a trend with these arguments as we move forward.

Now let’s do Shorthand JavaScript

elem = document.querySelector('#my_button')
elem.onclick(function(){
console.log('clicked')
})

Now here we eliminate a bit of code so that we have a shortened version of our event listener. We can return the boolean at the end of our function if we so desire.

Well what’s nice, we can get rid of a bit of code, convenient much?

But we can make it event shorter!

Not by much though

document.querySelector('#my_button').click(function(){
console.log('clicked')
})

Yes that’s right folks! We can just use the “.click” notation. We saved a couple key strokes, but is that really all there is to it?

No actually, not so, in my testing I actually found that my “.click” Event Listeners got executed before my “.onclick” Event Listeners, puzzling right? I felt the same way. They should boil down to the same call, shouldn’t they?

Well I actually didn’t end up figuring out why it works the way it does. The verdict is still out on that one, but I just wanted to make sure I passed on that knowledge to you.

Now..let’s get spicy with some jQuery!

Okay, honestly not much difference here…

$('#my_button').on('click', function(){
console.log('clicked')
})

We can use our “.on” function in jQuery to register our event listeners.

But we don’t have to!

$('#my_button').click(function(){
console.log('clicked')
})

Yes, I’m sure you’re seeing the trend here. There is much similarity between raw JavaScript and jQuery, and that makes it convenient to learn the framework if you choose to use it. On the other hand you might say, why bother? But let’s not get into that, I like it :).

Moving Along..

There are many ways we can mix and match these calls, but I think you’ve got the idea by now. Let’s move into the more interesting nuances with Event Listeners.

Let’s look at the scenario where elements are added to the DOM after the initial page load. In simpler terms, you load the page, but then you have some JavaScript, that adds a div with some other elements after.

For a practical example let’s say you’re building a blog or chat app, and messages and new blogs are loaded via JS. There are buttons loaded along with these messages to interact with them, maybe you want to comment, reply directly to messages or event delete them. Let’s look at the code below

$('#msg_delete_button').click(function(){
console.log("I'm being de-")
})

The code above would not work for deleting the message, but why not?

Well, that’s because the message was loaded after the page was already finished being loaded. This means that the event listener that you had set up on the page, was not linked to this element, only to the elements that were already on the page when this code was processed by the browser.

Let’s say a class of 20 kids starts at 10AM, but 1 kid is late. 19 Kids get instructions and understand what to do when they’re called on. Think of the late kid that doesn’t know what to do when they’re called on, thats exactly what’s happening with your elements if they’re added to your webpage afterwards.

Now you might be thinking the solution would just be to “re-register” the event listener. But be careful, you can run into the duplicate listener issue there. If you once again pass the same code to your browser, there could be two of the same event listeners attached to the elements, causing more issues.

So how do you get around this?

Well, you can actually listen to the parent element, let’s say the chat stream/body div, or the entire body, like so:

$('body #msg_delete_button').click(function(){
console.log("I'm being de-")
})
//OR
$('body').on('click', '#msg_delete_button', function(){
console.log("I'm being de-")
})

This is called event delegation.

When you click an element on a page, you’re still clicking the page. When you open a door, you have to touch the door knob. Touching the door knob, means you’re touching the door. It’s pretty much like that.

So to click a button, you have to click the body of the page. That’s why this works, you listen to the clicking of the entire body, and if the specific element that is clicked has that id attribute, then the event listener is executed.

Of course, you don’t have to listen to the body, you can listen to any parent element, the document or even the window.

In plan JS, event delegation can look like this:

document.addEventListener('click', function(event) {
if (event.target.matches('#msg_delete_btn')){
console.log('I'm being de-');
}
}, false);

Great! Now we know how to use our JS Event Listeners!

Yeah..you know what’s coming, I won’t delay any further. So, remember that jQuery snippet from earlier showing event delegation?

This won’t always work.

Wait, what? You’re telling me even if I listen to the entire body, I’ll miss something? How is that even possible.

Well worry not, there’s an easy fix, you can listen to the document and rewrite your listener like so :)

$(document).on('click', 'body #msg_delete_button', function(){
console.log("I'm being de-")
})

You’re probably wondering in what situations will the body listener not work so you can avoid it. Well, in my personal testing, I’ve found that it falls apart when I add multiple layers of DOM elements.

  1. Let’s say I load page
  2. Then I load a list of blogs
  3. Then when a button is clicked on one of these blogs divs, a modal is appended and pops up
  4. Then buttons on those modals have event listeners as well which causes DOM changes.

Yeah, that’s essentially the gist of it.

But why I am adding so many things to the DOM after the fact? Well I try to optimise user experience and reduce data usage. It’s because I want to save the user time and money, less data usage and a faster site contributes to a better user experience.

Also when building Single Page Applications (SPA’s) and Progressive Web Applications (PWA’s), you’ll be adding a lot of things to the DOM. PWA’s is what I focus on, so it’s a big part of my web development flow.

I hope you’ve enjoyed the read and learnt a thing or two about event listeners, thanks for reading…till next time!

--

--

Romario Fitzgerald

I’m a young software developer and entrepreneur who is always looking for ways to grow.