DEV Community

Cover image for Bubbling and capturing JS events
Pierre
Pierre

Posted on • Edited on • Originally published at daolf.com

Bubbling and capturing JS events

or should I say, things you should know about DOM events.

If you had the chance to do some client-side JS you certainly had to handle events. While libraries such as Jquery have made it very easy and simple to do so, there are still some shady parts that I think are worth looking into.

The event object

Let’s begin by capturing a dummy event object and take a look into it, it is as easy as doing this :

document.body.addEventListener(click, function(event) {
    console.log(event);
});
Enter fullscreen mode Exit fullscreen mode

If you look at the object you should end up with something like that :

You can see a lot of properties related to the actual position of the event. The isTrusted property indicates that the event was generated by an actual user and not a script. Today we are not going to cover them all, but only the four inside the red rectangles.

useCapture, or the unknown parameter

So this is how I added the event listener to my body :

document.body.addEventListener(click, function(event) {
    console.log(event);
});
Enter fullscreen mode Exit fullscreen mode

This is what my DOM looked like during the capturing of this event :

<body>
    <div id="div1">
        Click me!
    </div>
</body>
Enter fullscreen mode Exit fullscreen mode

So by clicking on a div tag, a handler attached to the body was executed, how is that possible? I mean, I clicked on a div tag, not on the body. Well, the first answer that comes in your mind could be:

“Events traverse dom, end of the story, nothing worth writing an article’’

And you are right, events do traverse the dom, but in what order? I mean, think about it. Two orders are possible, and both equally make sense.

  • The dom order (unofficial name)

So the DOM is a tree right, and to traverse a tree you go from the root to the leafs right? So in my case, the body tag would be the root, and the div tag would be the leaf, does that seem acceptable?

“Hum why not? But what about the other approach’’

  • The UI order (unofficial name)

You could also argue that because you, as a user, see the div above the body, and therefore click on the div and not the body, the event could go from, the div to the body. From leaves to the root. And that would make sense too.

“ Yes, this makes more sense, and this is what I observed, where is the truth ?’’

The truth is at W3C, let’s review it together and let’s take a look at the addEventListener method.

[http://www.w3schools.com/jsref/met_element_addeventlistener.asp](http://www.w3schools.com/jsref/met_element_addeventlistener.asp)http://www.w3schools.com/jsref/met_element_addeventlistener.asp

You see that third boolean parameter, this is where all the magic happen. By default this parameter is false, which means, following the semantic, that by default we do not use the capture. Note that this third argument is the reason I couldn’t write this post with Jquery. The click() method (or any other event related method), does not take a third parameter.

“ But what is capture ?’’

Capture is a mode and would be what we referenced before as the DOM order. The other mode, the default mode, is the bubbling mode, the UI order if you prefer. Those two modes will decide if the handler is executed during the capturing phase or bubbling phase.

The bubbling and the capturing phase

When you click on a document, the event first comes from the root of your DOM, the Window node, to the leaves, this is the capturing phase. Once the event has reached the leaf, like a bubble in water that tries to go back to the surface, the event goes back to the root of the DOM, this is the bubbling phase.

courtesy of W3C

By setting this third parameter you simply tell your DOM element to execute the handler during the bubbling phase or during the capturing phase. Because the parameter has a default value that covers most of the use case, it has become quite forgotten. By using it right we can have a lot more control over our event as I’ll show you.

What would happen if we add an event listener, for each phase, in what order would they get triggered? Let's change the code a little bit.

//Capturing phase
document.body.addEventListener("click", function(event) {
  console.log(" body capturing");
}, true);

document.getElementById("div1").addEventListener("click", function(event) {
  console.log(" div1 capturing");
}, true);

//Bubbling phase
document.getElementById("div1").addEventListener("click", function(event) {
  console.log(" div1 bubbling");
}, false);

document.body.addEventListener("click", function(event) {
  console.log(" body bubbling");
}, false);
Enter fullscreen mode Exit fullscreen mode

And as expected, this will be the trace if we click on the div:

You can go and check by yourself here (don’t forget to open the console).

As you can see this is very simple, this third parameter allows you to tell if the outer divs should execute the handler before or after the inner divs. Note that at every moment you can tell the event to stop propagating in one mode or the other by using :

event.stopPropagation()
Enter fullscreen mode Exit fullscreen mode

CurrentTarget and target

Now that you understand that events traverse the DOM in both directions there is one question that is still difficult to answer.

How in my handler can I know where is the event coming from?

For example in our handler attached to the body, what if I want to execute the handler if we click on the body, and only on the body, not in div above. This is exactly a case where you could use currentTarget and target.

currentTarget is great, currentTarget will always have the value of the DOM element which has the event listener attached to. It means that in our case, currentTarget will always have the body element as value.

target will have the value of the DOM element which receives the event in the first place (the element under your mouse). So if you want the handler to be executed only when the body is “really” clicked you can do something like that :

document.body.addEventListener("click", function(event) {
  // Target and currentTarget are the same
  // You clicked on the body
  if(event.target === event.currentTarget) {
    // the behavior you want
  } else {
    // default behavior
  }
}, false);
Enter fullscreen mode Exit fullscreen mode

Thank you for reading:

I hope you now know more about JS events than 5 minutes earlier.

Please tell me in the comments if you have any more question regarding JS events and don't forget to subscribe to my newsletter, there is more to come :) (And you'll also get the first chapters of my next ebook for free 😎).

Things you might like:

You can read the part 1 of my new GIT series that people around here seemed to love, it talks about the infamous .git directory.

Or something about Python here ;)

Top comments (0)