Javascript Events, Capturing works Bubbling doesn't

My understanding of JS event propagation is that an event first "captures" down the DOM tree, then "bubbles" back up, triggering handlers along the way.

<html>
<body>
    <div id="textbox">
        nothing yet
    </div>
</body>

<script>
    // Gets incremented by "update" event
    var val = 0;

    // Event starts here
    textbox = document.getElementById("textbox");
    textbox.addEventListener("update", function(e) {
        textbox.innerHTML = val;
    }, false);

    // Should bubble here
    body = document.getElementsByTagName("body")[0];
    body.addEventListener("update", function(e) {
        val++;
    }, false); 

    function update() {
        var e = new Event("update");

        textbox.dispatchEvent(e);
    }

    setInterval(update, 10);
</script>
</html>

In my code here there is a div "textbox" inside the body. I think the update event sent to the textbox should bubble up to the body, but it doesn't. The counter never updates.

If I set the UseCapture argument of the body's event listener to true, the counter updates.

Why does capturing work, but not bubbling?

Answers:

Answer

dispatchEvent dispatches your event. Summarizing, that means

When an event is dispatched to an object that participates in a tree (e.g. an element), it can reach event listeners on that object's ancestors too.

First all object's ancestor event listeners whose capture variable is set to true are invoked, in tree order.

Second, object's own event listeners are invoked.

And finally, and only if event's bubbles attribute value is true, object's ancestor event listeners are invoked again, but now in reverse tree order.

Therefore, if the bubbles attribute is not true, the event won't bubble.

You can set the bubbles attribute to true when you create the event with Event:

event = new Event(type [, eventInitDict])

Returns a new event whose type attribute value is set to type. The optional eventInitDict argument allows for setting the bubbles and cancelable attributes via object members of the same name.

Answer

To get the effect you want (show nothing yet, then 0,1,2,...) you need to follow a couple of the previous answers, plus set capturing on the textbox. (otherwise you'll see show nothing yet, 1, 2,...). First you need to set bubbling to true on your event - like this:

function update() {
    var e = new Event("update",{bubbles:true});
    textbox.dispatchEvent(e);
}

Then you need to capture the event (set to true) - like this:

// Event starts here
textbox = document.getElementById("textbox");
textbox.addEventListener("update", function(e) {
    textbox.innerHTML = val;
}, true);

So everything looks like this:

// Event starts here
textbox = document.getElementById("textbox");
textbox.addEventListener("update", function(e) {
    textbox.innerHTML = val;
}, true);

// Should bubble here
body = document.getElementsByTagName("body")[0];
body.addEventListener("update", function(e) {
    val++;
    console.log(val)
}, false);


function update() {
    var e = new Event("update",{bubbles:true});
    textbox.dispatchEvent(e);
}

setInterval(update, 1000);
Answer

You need to pass { bubbles: true } as the second argument to new Event( ... ) for the Event to bubble, as per the documentation on MDN, because bubbles defaults to false.

Updated jsfiddle example

Answer

If you use the old (unfortunately deprecated) way, it does bubble. See https://jsfiddle.net/py9vyr7h/1/

function update() {
    var event = document.createEvent('CustomEvent');
    // Second param says to bubble the event
    event.initEvent('update', true, true);
    textbox.dispatchEvent(event);
}

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.