Event bubbling/capturing - where does it start/end? - javascript

I understand that an event has two modes -- bubbling and capturing.
When an event is set to bubble, does Javascript checks up to "document"?
When an event is set to capture, does Javascript always starts from "document"?
How does Javascript know where to stop/start?
Let's say I have the following code in my body tag.
<div id='outer'>
<div id='inner'></div>
</div>
When I set an event to #inner to bubble, does Javascript check up to document or does it stop at #outer?

From W3C Document Object Model Events
I know I'm nitpicking but it isn't javascript that handles the events you are describing, it is the DOM-engine (Document Object Model). In the browser there are bindings between the javascript and DOM engines so that events can be propagated to javascript, but it is not limited to javascript. For example MSIE has support for BASIC.
When an event is set to bubble, does Javascript checks up to "document" ?
1.2.3 "This upward propagation will continue up to and including the Document"
"Any event handler may choose to prevent further event propagation by calling the stopPropagation method of the Event interface. If any EventListener calls this method, all additional EventListeners on the current EventTarget will be triggered but bubbling will cease at that level"
When an event is set to capture, does Javascript always starts from "document"?
1.2.2 "Capture operates from the top of the tree, generally the Document,"

Event bubbling
JavaScript checks all the way up to document. If you add a listener on document and a listener on inner, both listeners fire.
Event capturing
JavaScript starts from document and goes all the way down to inner. If you add a listener on document and a listener on inner, both listeners fire.
My Findings
Turns out that the browser does some sort of smart processing so that it
a) doesn't have to loop through the entire parent hierachy
and
b) doesn't have to loop through all events.
Proof
a) It takes the browser no time to trigger both click events when the inner div is clicked:
Fiddle
b) It takes the browser no time to trigger both click events when the inner div is clicked when lots of other events exist that are attached to other DOM elements not in the parent hierachy:
Fiddle

Partial answer..
1 - When an event is set to bubble, does Javascript check up to "document" ?
Not if one of the elements in the hierarchy decides to stop the bubbling by calling stopPropagation()

Related

View DOM level 2 Event handlers in IE

Is there any way to view DOM level 2 event listeners added to a DOM element in IE 9+?
In Chrome, we can see attached events from console using getEventListeners(object).
I tried Visual Events, but it only displays DOM level 0 events.
In case if you are wondering, I need to list the attached event handlers to window unload event and debug which events are fired to find out which one is causing an exception/preventing propagation. The unload event handler is getting dispatched properly in Chrome.
Yes, you can easily see DOM2 handlers:
Right-click the element with the event handler and choose Inspect Element
That should trigger the DOM Explorer tab; if not, do so
Pick the Events tab on the right-hand side
It lists the event handlers attached to the element, including DOM2 ones.
For instance, using this fiddle:
<div id="target">
I have a DOM2 event handler.
</div>
function thisIsADOM2Handler() {
this.style.color = "green";
}
document.getElementById("target").addEventListener("click", thisIsADOM2Handler, false);
I followed the steps above to see this:
I need to list the attached event handlers to window unload event
You'll find the handlers for the window unload event listed on the body element, so navigate there in the DOM Inspector to see them:

Why do events bubble up the dom?

I understand event bubbling and how it traverses up the dom from the inner-most element. I am curious as to why this is the default behaviour?
<div>1
<div>2
<div>3
<div>4
<div>5</div>
</div>
</div>
</div>
</div>
If I have an event listener on each part but click the <div>5</div> why does the event bubble up to div4, div3, div2(etc)'s event listeners?
EDIT: I don't see this as a duplicate of "what is event bubbling" because this is asking why not what
If you're asking why this is the default behaviour, then the answer is because the language specification says so (emphasis mine):
An event listener consists of these fields:
[...]
capture (a boolean, initially false)
[...]
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.
If you're asking why the specification was defined this way, you can ask yourself:
If you've clicked on div5, have you also clicked on div4 or not?
That is ultimately a matter of opinion, but for me the answer is yes, as per my initial comment:
If you've been to Geneva, you've been to Switzerland as well.
Your statement is wrong. The default for simple events is not to bubble:
Firing a simple event named e means that a trusted event with
the name e, which does not bubble (except where otherwise
stated) and is not cancelable (except where otherwise stated), and
which uses the Event interface, must be created and
dispatched at the given target.
So the HTML related events which bubble it's because the spec explicitly says so, presumably because it makes more sense this way.
Some events which bubble:
Click events:
Firing a click event means firing a synthetic mouse event named click, which bubbles and is cancelable.
Unfocusing steps:
fire a simple event that bubbles named change at the element.
Some events which do not bubble:
Error events when updating image data
fire a simple event named error at the img element
Readystatechange events when changing the current document readiness
fire a simple event named readystatechange at the Document object.

Capturing and Bubbling using jQuery

I am new to jQuery and I‘m trying to understand the concept of capturing and bubbling.
I have read a lot of articles, but most of them described event propagation for Javascript.
Lets assume we have the following HTML code:
<div id="outer">
outer
<div id="inner">
inner
</div>
</div>
Capturing is the phase where we go down the DOM elements and bubbling is when we go up.
In Javascript you can decide which way to follow (using true or false parameters):
element.addEventListener('click', doSomething, true) --> capture phase
element.addEventListener('click', doSomething, false) --> bubble phase
Is there anything similar for jQuery to denote which way to follow other than the JavaScript way?
Also does jQuery uses a default phase? For example bubble?
Because i used the following code to test this:
css
<style>
div {
border: 1px solid green;
width: 200px;
}
</style>
jQuery
<script>
$(document).ready(function(){
$('div').click(function(){
$(this).animate({'width':'+=10px'},{duration: 3000})
});
});
</script>
It appears that when I click on the outer div, only that div animates to a larger div. When I click to the inner div both divs animate to larger divs.
I don’t know if I am wrong, but this test shows that the default browser propagation method is bubble.
Please correct me if I’m wrong.
jQuery only uses event bubbling. If you want to add an event handler that uses the capturing model, you have to do it explicitly using addEventListener, with the third argument true as you show in the question.
Event bubbling which will start executing from the innermost element to the outermost element.
Event Capturing which will start executing from the outer element to the innermost element.
But jQuery will use event bubbling. We can achieve event capturing with:
$("body")[0].addEventListener('click', callback, true);
The 3rd parameter in the addEventListener which will tell the browser whether to take event bubbling or event capturing.
By default it is false.
If it is false then it will take event bubbling.
If it is true then it will take event capturing.
Question and answers live with the following misconception: that the browser does either capture or bubble.
Truth is: the browser does always both, capture and bubble, on every click, in that order.
Is there anything similar for jQuery to denote which way to follow other than the JavaScript way?
Also does jQuery uses a default phase? For example bubble?
jQuery has no event phases. The DOM has. And the DOM does always both.
But jQuery registers handlers only to the bubble phase. There is no jQuery way to register to the capture phase, so bubble registration is not a default, it is the only way (with jQuery).
I don’t know if I am wrong, but this test shows that the default browser propagation method is bubble.
You are wrong, if I’m allowed to say. When you click on the outer div, capture happens, until it reaches the outer div, then bubble... It just does not go any deeper than the actual target of the event.
If you click the inner div, capture passes the outer div, but no handler is registered there for that phase, then it reaches the target, and on the way back up (bubble) it triggers the outer div handler.—I haven’t run your code, but it would be hard to tell which one happened first (the inner is first).
(Note: once the target is reached, the phase is actually called “target phase” and handlers are called independent of which phase they registered for (in registration order, btw).)
Every Event is going first through "capturing" phase and then through "bubbling" phase.
For instance, when user clicks on <a>, all event handlers bound using "capturing" (third argument in addEventListener method set to true, not supported in jQuery) are called starting from outermost <html> all the way down to the link. Then, the "bubbling" phase starts and all event handlers using "bubbling" (supported in jQuery) are called the opposite way - from link back to the <html>.
You can try it on your own, firing this code in developer tools and clicking anywhere on your site.
document.querySelectorAll("*").forEach(it => {
it.addEventListener("click", function() {console.log("capturing: ", it)}, true);
it.addEventListener("click", function() {console.log("bubbling: ", it)}, false);
});
The event is triggered in event bubbling on the element on to which the user has clicked,and unless we call .stopPropagation() on the event object the event is then triggered all the way up the DOM.
Default is event bubbling set in Jquery in order to use Capture ypu need to set parameter as true in .addEventListner

Add on load event for img elements now and in future?

I want to add a load event on an image that would affect that image even when that image is added to the document after the page's initial load. For a click event I would do someting like this:
$(document).on('click', '.elem', function(e) {
// do stuff
});
When I try something similar with the load event, however, it does not to work. This is what I have tried:
$(document).on('load', '.image', function() {
// do stuff
});
This event is simply never triggered. Does anyone know what I may be doing wrong, or how to achieve this?
This answer is incorrect. It's possible to do this using the capture phase, see Dhia Louhichi's answer. I'll delete this answer when I can (i.e.., once it's no longer the accepted answer).
By their nature, delegated handlers only work for events that bubble. Not all do, and load is one of the ones that doesn't. The jQuery documentation even highlights this:
In all browsers, the load, scroll, and error events (e.g., on an element) do not bubble.
You'll have to add the load handlers to the images when you add them.
What I mean by "delegated handlers only work for events that bubble":
Events that bubble work like this (in the "bubbling" phase, which is the phase you normally work with): The event is fired on the element where it originates, and then on that element's parent, then that element's parent, etc. until it gets to the document element (html). This diagram from the DOM3 events spec may help make this clearer:
Using a delegated handler (the kind you're using in your question) relies on bubbling. Consider this HTML:
<div id="container">
<div class="content">xxxx</div>
<div class="content">xxxx</div>
<div class="content">xxxx</div>
</div>
If you do $("#container").on("click", ".content", ...) you're not hooking the event on the "content" divs, even though jQuery will make it seem a bit like you are. You're hooking the event on the "container" div. When the event bubbles down to the container div, jQuery looks at where it started and sees whether it passed through any "content" divs during its bubbling. If it did, jQuery calls your handler as though (mostly) you'd hooked the event on the "content" div. That's why delegated handlers work when you add elements later; the event isn't hooked on the element, but on the container.
So that's why it won't work for load: load doesn't bubble, so even though it fires on the img elements you add, it doesn't bubble to the parent and so on, and so you never see it. To see it, you have to hook it on the specific element, not an ancestor of it.
This code shows handling the load event for img elements created in the future, without explicitly adding a listener/handler to them, by using the capture phase of the event process on document.body (also works when attached to document, but not window because of backward compatibility issues):
document.body.addEventListener(
"load",
function (event) {
var elm = event.target;
if (elm.nodeName.toLowerCase() === 'img') {
console.log("Loaded: " + event.target.src);
}
},
true // Capture phase
);
Live Example:
document.body.addEventListener(
"load",
function (event) {
var elm = event.target;
if (elm.nodeName.toLowerCase() === 'img') {
console.log("Loaded: " + event.target.src);
}
},
true // Capture phase
);
// Brief wait, then add an image
setTimeout(function() {
document.body.insertAdjacentHTML(
"beforeend",
"<img src='https://via.placeholder.com/150/202080?text=Some+Image'>"
);
}, 400);
This is tested and works in at least the following:
IE9+
Chrome and other Chromium-based browsers (Opera, Edge, Vivaldi, ...)
Firefox
iOS Safari
The behavior is also documented. In fact, coincidentally the specification gives this example mentioning load by name (scroll down slightly from that link):
EXAMPLE 5
The following is one way to interpret the above tables: the load event will trigger event listeners attached on Element nodes for that event and on the capture and target phases. This event is not cancelable. If an event listener for the load event is attached to a node other than Window, Document, or Element nodes, or if it is attached to the bubbling phase only, this event listener would not be triggered.
That's saying load will be fired in the capture and target phases, but not the bubbling phase (since the event doesn't bubble).
By default, when you use addEventListener, the handler is attached for the target phase of the element you call addEventListener on and the bubbling phase for any element within that element. If you add the third argument with the value true, though, it attaches the handler for the target phase of the element you call addEventListener on (as before) and the capture phase for any element within that element. So the code above will handle load for document.body during the target phase (except document.body doesn't fire load) and also handle load for the capture phase of any element within document.body.
More about event flows in the specification, including this handy diagram:

How to attach multiple event handlers to addEventListener() method and what is bubbling.vs.capturing differences?

How do i attach multiple event handlers to addEventListener() method
For eg:
elem.addEventListener("mouseover",handlers....,false/true);
I also wanted to know how the bubbling when set to false and capturing when set to true on the 3rd parameter does in the dom tree.
Event bubbling means that when an event is triggered on an element, this element's parent is then checked for an event of the same time. If one exists, it is also triggered. This process is then repeated all the way up through the DOM tree. I believe that bubbling events will also make use of capturing (see below) before the bubbling process.
Capturing starts from the root and traverses the DOM tree down to the target element, triggering events of the target's type on the way.
For a better understanding of this, you can find more information here.
As for your first question, it has been a while since I worked with this, so therefore I do not remember a way to do what you want in one statement. However, you could do something like this:
myElement.addEventListener('click', myFunction1, false);
myElement.addEventListener('click', myFunction2, false);

Categories

Resources