A note about event bubbling

Just now I delivered a project during the making of which I noted a feature of event bubbling that, though totally logical, came as a surprise to me. No doubt someone else will be surprised, too, and may even be able to use it in a project.

The project was an adaptation of my Site Survey script that's treated in chapter 6 of the book. In the standard script, a survey popup is opened as soon as the user clicks anywhere. However, for this version of the script I had to implement a special bar with a Yes/No option in which the user can indicate whether he wants to participate in the survey. Only when the user clicks Yes (or when he doesn't answer on three consecutive pages) is the popup opened.

I open the popup through my addEventSimple script:

addEventSimple(document,"click",ST_openPopup);

Now the event is set for the bubbling phase: ie. any time a user clicks anywhere on the page, onclick event handlers of the element she clicks on are handled first (if any), after which the click event bubbles up to the document and my ST_openPopup() function is executed. Usually I set this event handler immediately, but in the current version I set it only after the user has agreed to the survey.

The Yes/No buttons both have an onclick event handler, too. Appropriate actions are taken, and then the special bar with the buttons disappears.

The appropriate action on the Yes button is, among others, setting the onclick event handler on the document. When I tested this bit of the script, the popup opened as soon as I clicked on the Yes button. I'd expected I'd have to code this explicitly, but event bubbling took care of the dirty bits for me. What a pleasant surprise.

Picture the situation:

  1. Initially there's a button with an onclick event handler. The document doesn't have any.
  2. The user clicks on the button and generates a click event.
  3. As soon as the event enters the bubbling phase (I never use the capturing phase) the onclick event handler of the button is executed. Among other things, it sets an onclick event handler on the document.
  4. When the event handler is done the event bubbles up to the document.
  5. Lo and behold, it finds an event handler, which is executed, and the popups pops up.

Useful, isn't it? And totally logical, once you stop to think about it.

However, later in the project I removed the button bar onclick, too. All of a sudden the popup stopped appearing as soon as I clicked on the Yes button. That's logical, too, though it took me five anguished minutes to track down the source of this apparent bug.

  1. Initially there's a button with an onclick event handler. The document doesn't have any.
  2. The user clicks on the button and generates a click event.
  3. As soon as the event enters the bubbling phase the onclick event handler of the button is executed. Among other things, it sets an onclick event handler on the document and removes the button from the document.
  4. Now the event handler can't travel any further. The cause could be that the entire event object is destroyed. It could also be that the event just stops bubbling because the button has been taken out of the document tree and there's nothing to bubble up to.
  5. In any case the document's onclick is not executed. The popup appears only on the user's next click.

The solution was simple: delay the removal of the button bar by 100 milliseconds (setTimeout). Now the event first bubbles up to the document and the popup is opened. Afterwards the bar can be safely removed.

All in a day's work.

This is the blog of Peter-Paul Koch, web developer, consultant, and trainer. You can also follow him on Twitter or Mastodon.
Atom RSS

If you like this blog, why not donate a little bit of money to help me pay my bills?

Categories:

Comments

Comments are closed.

1 Posted by Leen Besselink on 26 January 2007 | Permalink

The length of the setTimeout in miliseconds really shouldn't matter.

You can set it to 1. Less than 1 does not work/make no sence.

All the events (that have a handler) including, timeout-events from setTimeout, mouse- and keyboard-events, async. http-response-events, etc. are all saved in the same eventQueue.

When it's still busy handling the click event, it won't handle any other events.

2 Posted by Julien Royer on 26 January 2007 | Permalink

This behaviour seems to be a bug :

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-flow-bubbling
"The chain of EventTargets from the event target to the top of the tree is determined before the initial dispatch of the event."

3 Posted by Jan Vermaat on 26 January 2007 | Permalink

I agree with PPK it is 'totally logical' behaviour.

In fact I knew this for a long time, but I never used it. I don't think it is a bug, Julien.

The chain of eventTargets is determined before the dispatch of the event, but when you delete a node from the chain, the chain is broken.

4 Posted by Julien Royer on 26 January 2007 | Permalink

> The chain of eventTargets is determined before the dispatch of the event, but when you delete a node from the chain, the chain is broken.

The DOM Level 2 Event Model also specifies :
"If modifications occur to the tree during event processing, event flow will proceed based on the initial state of the tree."

I think it is pretty clear, isn't it ?

5 Posted by ivan on 27 January 2007 | Permalink

theoretically speaking, instead of using timeout, you could also set onclick event on button to fire on capturing phase, too, and use that event to remove the button. It would be even more elegant solution, but I guess that would create cross-browser problems..

6 Posted by Ralph on 30 January 2007 | Permalink

The exact same thing happened to me recently replacing the submit of a form by an element with a (nice) "Working..." message, I removed the button from the document (to avoid any more submits) and the submit event stopped bubbling, what I did was to hide the button instead of removing it —of course I took other measures as well). Didn't thought about a delayed step, though, but judging from Julien's link, my reasoning wasn't wrong, even though I had no idea about it :)