Form label event bubbling

Extra

Another JavaScript-related article in the same month? What is going on?


You may have heard of event bubbling and capturing. According to javascript.info, this is how event bubbling works:

When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.

That knowledge serves you well. Given that this is all about ancestors, sibling elements are never affected.

However, there are HTML elements with a special relationship, I’m talking about <label> and any interactive element that a label can be applied to, e.g. an <input>. While they can be nested, in which case the label is an ancestor of the labeled element, they can also be tied together using the for attribute on the label, like so:

<label for="field">Label:</label>
<input id="field" name="field" type="text">

Label and labeled element are usually siblings, but if necessary they may live far from one another in remote parts of the DOM.

How does event bubbling on a labeled element work? Unsurprisingly, no difference to what we’ve already heard. The web browser runs the handlers on it, then on its parent, then all the way up on other ancestors.

How does event bubbling on a <label> element work? The web browser runs the handlers on it, then on its parent, then all the way up on other ancestors. Here comes the twist: It does not stop there. After reaching the topmost element, bubbling starts over at the labelled element, and then all the way up its ancestors too. It is almost as if you click the label, and the browser clicks the labelled element for you.

How does event bubbling on a <label> with nested labeled element work? Same as before, there are no shortcuts. This means clicking on the label will trigger its handlers twice: Immediately, and once again when it bubbles up from the labeled element.

stopPropagation

This is where event.stopPropagation() comes in. Use can call it at any point in the bubbling phase to bring it to a halt. In other words, after stopPropagation is executed, handlers of elements further up in the DOM won’t be called.

With one caveat, which should already be clear, given what we just went through. When clicking a label, you may have to stop the bubbling twice, as it bubbles up for both label and labeled element. So next time an event handlers get called expectedly, maybe this is the reason.

I made a CodePen to illustrate all that. It has three areas: First, two checkboxes to turn stopPropagation on and off; second, a form with two kinds of label & labeled element combos; and third, a log that shows which event handlers are called when you click something on the form. Take a look.