Why do events bubble up the dom? - javascript

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.

Related

How does event bubbling work on event handling?

I have defined this event handler:
document.addEventListener("load", function(){
alert("Called on page load");
}, false);
I noticed it does not get called when the boolean flag is set to false(fire at bubble phase). Could someone help me out on why this is the case.
When an event is being sent to an element, it descends the document tree in the capture phase until it reaches the target. Then, if it’s a bubbling event, it bubbles back up.
From 2.1 Introduction to “DOM Events” in the DOM standard:
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.
load isn’t a bubbling event, and – here’s the important part – it doesn’t target document. When you add a capture listener, you’re really getting load events from parts of the document’s content that normally receive the event, like scripts or images. On a page with only the script, you won’t see the listener get called at all:
<iframe srcdoc="<script>document.addEventListener('load', () => { alert('loaded'); }, true);</script>"></iframe>
And on a page with load events that fire after the listener is attached, like this Stack Snippet that includes <style>s, you’ll see it more than once:
let i = 0;
document.addEventListener('load', e => {
console.log(`loaded ${++i}: ${e.target.nodeName}`);
}, true);
You probably meant to add a non-capturing listener to window instead of document, because window is something that receives a load event, unlike document. (Or you might have meant something else. There’s a lot of ways to interpret “page load”. See Window: load event on MDN for details on what the load event means on window and alternatives if it wasn’t what you intended.)
window.addEventListener("load", function() {
alert("Called on page load");
}, false);

How to ONLY trigger parent click event when a child is clicked

Both child and parent are clickable (child could be a link or div with jQuery click events). When I click on child, how do I only trigger parent click event but not the child event?
DOM Event Phases
Events have three phases:
Capture: The first phase is "capture" where event handlers are called starting with the <window> and moving down through descendants towards the target of the event.
Target: The second phase is the "target" phase when the event listeners on the target are called.
Bubbling: The third phase is "bubbling" which starts with the handlers listening on parent of the target being called first, then, progressively, the ancestors of that element.
Events also have a "default action", which happens after the bubbling phase. The default action is the browser-defined action that normally occurs for events of the specified type on the kind of element which is the target of the event (e.g. the browser navigating to the href of an <a> upon a click, whereas a click on another type of element will have a different default action).
The DOM Level 3 Events draft has a diagram that graphically shows how events propagate through the DOM:
Image Copyright © 2016 World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang). http://www.w3.org/Consortium/Legal/2015/doc-license (Use permitted per the license)
For more information, on capture and bubbling, see: "What is event bubbling and capturing?"; The DOM Level 3 Events draft; or W3C DOM4: Events
Preventing the event from getting to the child
For what you want, to get the event on the parent prior to, and prevent, the event on the child, you have to receive the event in the capture phase. Once you have received it in the capture phase, you have to stop the event from propagating to any event handlers on elements lower in the DOM tree, or which have registered to listen in the bubbling phase (i.e. all listeners on elements/phases which would be visited by the event after your listener). You do this by calling event.stopPropagation().
Receiving events during the capture phase
When adding the listener with addEventListener(type, listener[, useCapture]), you can have the useCapture argument be true.
Quoting MDN:
[useCapture is] A Boolean that indicates that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. Events that are bubbling upward through the tree will not trigger a listener designated to use capture. Event bubbling and capturing are two ways of propagating events that occur in an element that is nested within another element, when both elements have registered a handle for that event. The event propagation mode determines the order in which elements receive the event. See DOM Level 3 Events and JavaScript Event order for a detailed explanation. If not specified, useCapture defaults to false.
Preventing other handlers getting the event
event.preventDefault() is used to prevent the default action (e.g. prevent the browser from navigating to the href of an <a> upon a click). [This is used in the example below, but has no real effect as there is no default action for text. It's used here because most of the time when you are adding a click event handler you want to prevent the default action. Thus, it's a good idea to be in the habit of doing so, and just not doing so when you know you don't want to.]
event.stopPropagation() is used to prevent any handlers on elements later in any of the event phases from receiving the event. It does not prevent any additional handlers on the current element and phase from being called. It does not prevent the default action from occurring.
event.stopImmediatePropagation(): Handlers on the same element and phase are called in the order in which they are added. In addition to having the same effect as event.stopPropagation(), event.stopImmediatePropagation() prevents any additional handlers on the same element and event phase from receiving the event. It does not prevent the default action from occurring. Given that the requirement for this question is to prevent the event from propagating to children, we don't need to use this, but could do so instead of using event.stopPropagation(). Note, however, that listeners on the same element are called in the order they are added. Thus, event.stopImmediatePropagation() will not prevent the event from being received by those listeners on the same element and phase as your listener which were added prior to your listener.
Example
In the following example, event listeners are placed on both the parent and the child <div> elements. Only the listener placed on the parent receives the event because it receives the event during the capture phase prior to the child and it executes event.stopPropagation().
var parent=document.getElementById('parent');
var child=document.getElementById('child');
var preventChild=document.getElementById('preventChild');
parent.addEventListener('click',function(event){
if(preventChild.checked) {
event.stopPropagation();
}
event.preventDefault();
var targetText;
if(event.target === parent) {
targetText='parent';
}
if(event.target === child) {
targetText='child';
}
console.log('Click Detected in parent on ' + targetText);
},true);
child.addEventListener('click',function(event){
console.log('Click Detected in child (bubbling phase)');
});
child.addEventListener('click',function(event){
console.log('Click Detected in child (capture phase)');
},true);
<input id="preventChild" type="checkbox" checked>Prevent child from getting event</input>
<div id="parent">Parent Text<br/>
<div id="child" style="margin-left:10px;">Child Text<br/>
</div>
</div>
jQuery
jQuery does not support using capture on events. For more information as to why see: "Why does jQuery event model does not support event Capture and just supports event bubbling"
Another option for this that may be useful in certain circumstances when you know that none of the child elements are interactive is to set pointer-events: none in your css (link). I usually apply it to all child elements of the element on which I want to capture interaction. Like this:
#parentDiv * {
pointer-events: none
}
Note the *, declaring that the rule applies to all children of the parentDiv.
Prevent the children from receiving the parent's click event:
parent.addEventListener('click',function(e){
e.stopPropagation();
console.log('event on parent!')
},true);
(Note that the second parameter is true)
Prevent the parent from receiving itself or it children's click event:
parent.addEventListener('click',function(e){
e.stopPropagation();
console.log('event on parent or childs!', e.target.closest('.parent_selector'))
});
e.stopPropagation means that stop next ones in the hierarchy to receive the event.
second argument (useCapture) is a flag, and means that reverse the order of receiving events. (use capture phase instead of bubble phase.).
it means if you set it to true, parent will receive the click event, then the child. (normally the child will get the event first.)
(see the #Makyen's answer for detailed explanation.)
To make life really simple and easy here i am
Use on parent node similar to this
target_image.addEventListener('drop',dropimage,true);
This will enable the parent child ancestor relationship and the same event will be called in for the parent and child.
To make the event only be called for the parent use the following code snippet in the event handler. First line
event.stopPropagation();
event.preventDefault();
You can use $event.stopPropagation() in the html file.
(click)="openAttendeesList(event.id,event.eventDetailId,event.eventDate) ; $event.stopPropagation();"
You can use the CustomEvents property on elements.
Create an event object and let the child element dispatch the event to its parent
see demo here
document.getElementById('parent').onclick = function() {
alert("you are clicking on the parent stop it");
}
document.getElementById('child').onclick = function(e) {
alert('I am sending this event to my parent');
event = new CustomEvent('click');
document.getElementById('parent').dispatchEvent(event);
}
#parent {
display: inline-block;
width: 100px;
height: 100px;
border: solid black;
}
#child {
border: solid red;
}
<div id=parent>
<div id=child>I am a child</div>
</div>

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

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

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()

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