For example we have a page with a link that has onclick event listener on it. But handler makes stopPropagation. How I can handle that click event was made, if it's not bubble to root anymore?
e.g.
document.addEventListener('click', function(e) {console.log(e);});
a.onclick = function(e) {e.stopPropagation();};
DOM event propagation works in three phases: the capture phase, the target phase and the bubble phase. Roughly, the event first works its way down to the target, reaches the target and then works its way back up.
By default, event handlers are attached in the final bubble phase. If you attach a click event listener in the first capture phase, it will run before the previous handler has had the chance to call stopPropagation.
See this question for a deeper explanation.
The simple answer is, add a third argument, true, when adding your event listener.
document.addEventListener('click', someFunction, true)
This flag (called useCapture) will call someFunction on all clicks in a document, even when the user clicked inside an element with a click handler that called event.stopPropagation.
With options
If you're already passing an object of options as the third argument, simply include capture: true in them:
document.addEventListener('click', someFunction, { capture: true, ...someMoreOptions })
Why?
Enabling the handler's useCapture mode like this means the listener listens during the earlier "capture" phase of the event (which starts at the outmost element then trickles down through children), instead of the later "bubble" phase (which starts at the innermost element and bubbles back up through ancestors, and is the one stopPropagation blocks).
Side effects
That also means that applying this setting changes the timing: your capture phase click event will occur before any click events of either type inside child or descendant elements.
For example, in the above function, if a user clicks on a button on the page, the someFunction attached to the document's capture phase will be called before any handlers attached to the button; whereas without setting use capture to true, you'd expect it to be called after.
Related
I can get elements to execute their handlers in the capturing phase like so:
elem.addEventListener('event', handler, {once: false, capture: true});
Setting the third argument to true also works - any obvious reason why? Does doing it this way have any side-effects?
The boolean parameter was the original way how it worked, before the options object was introduced. That's the reason why it (still) works: for backwards compatibility.
See docs:
Syntax
target.addEventListener(type, listener [, options]);
target.addEventListener(type, listener [, useCapture]);
You can either pass a boolean useCapture (this exists for a longer time already) or an options object which allows you to specify capture too but other things as well (e.g. once).
So, addEventListener(..., true) is the same as addEventListener(..., { capture: true }), and there are no side-effects.
The options object form exists since ~2016. Check on caniuse to see which clients support it.
If you don't have other event listeners that may do stuff to the event or page, then it generally doesn't matter where you attach your listener. If this is the only listener, you can attach it to the element, or one of its parents, or to the document or window, and to any of those in the bubbling phase or capturing phase - and it won't make a difference in most cases.
The primary use of a capturing listener is to set the order in which event handlers execute. When an event is dispatched to the element, it first captures downward from the window, firing capturing listeners on the window, then on the document, then on intermediate elements, until it reaches the deepest element that resulted in the event. At the deepest element, both capturing and bubbling listeners will run, in the order they were attached. Then, the event will start "bubbling" up, triggering bubbling listeners on that deepest element, then on intermediate elements, then on the document, and then on the window.
To illustrate, see how the capturing listener runs first here, when the listeners are attached to a parent:
document.addEventListener('click', () => console.log('bubbling'));
document.addEventListener('click', () => console.log('capturing (method 1)'), true);
document.addEventListener('click', () => console.log('capturing (method 2)'), { capture: true });
<button>click</button>
If the element the listener is being attached to is the same element that dispatches the event (for example, if you have a button with no children, which gets clicked), then capturing vs bubbling has no effect - all listeners will run in the order they were attached, regardless of useCapture.
Bubbling listeners can be used to prevent the event from bubbling upward and triggering parent listeners. Similarly, capturing listeners can prevent the event from capturing downward and triggering child listeners.
window.addEventListener('click', (e) => {
e.stopPropagation();
console.log('capturing, stopPropagation')
}, true);
document.querySelector('button').addEventListener('click', () => console.log('at target'));
<button>click</button>
Above, the capturing listener called stopPropagation on the event, preventing it from propagating downward to the child listener, which would've run later were it not for stopPropagation.
For the third argument to addEventListener, you can either use { capture: useCapture } or just useCapture as the argument, per the specification:
The addEventListener(type, callback, options) method, when invoked, must run these steps:
Let capture, passive, and once be the result of flattening more options.
Where "flattening more" does:
Let capture be the result of flattening options.
Where "flattening" does:
If options is a boolean, then return options.
Return options’s capture.
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>
I'm looking for a way in JS to stop a click event from continuing down the DOM to a nested child element (capture phase) when the parent element was clicked.
It would be the inverse of the e.stopPropogation() function to prevent a click event from bubbling up.
Is there a native JS function for this?
Edit 03/10
Link to example
Edit 03/11
Typo in the function call - it's stopPropagation(), not stopPropogation(). Thanks to #JackPattishall for the find.
yes there is native js function for capturing event in capture phase.
In all browsers, except IE<9, there are two stages of event processing.
The event first goes down - that’s called capturing, and then bubbles up. This behavior is standartized in W3C specification.
All methods of event handling ignore the caputiring phase. Using addEventListener with last argument true is only the way to catch the event at capturing.
elem.addEventListener( type, handler, phase )
phase = true
The handler is set on the capturing phase.
phase = false
The handler is set on the bubbling phase.
For better understanding of event capturing and bubbling you can follow this link
okey, One thing you and do is use the stopPropogation() at the top in Dom.
eg. you have a table(#table1) and elements(tr,td).
so, if i do:
table1.addEventListener("click",function(event){
event.stopPropagation();
console.log(this);
},true);
tableElem[2].addEventListener("click",function(event){
console.log(this);
});
tableElem[2].addEventListener("click",function(event){
console.log("hii");
});
The event will be intercepted at top and propogation will be stopped.
stopPropogation() stops propagation irrespective of on which step event is intercepted.
Could somebody please explain the bubbling and capturing phase with this code according to output of the 4 cases in code , on clicking div2 and div1
http://jsfiddle.net/JuKmM/9/
code:
function doSomething2(){
console.log("div 1 clicked");
}
var element1=document.getElementById('div_1');
var element2=document.getElementById('div_2');
element1.addEventListener('click',doSomething2,false);
element2.addEventListener('click',doSomething,true);
//element1.addEventListener('click',doSomething2,false);
//element2.addEventListener('click',doSomething,false);
//element1.addEventListener('click',doSomething2,true);
//element2.addEventListener('click',doSomething,true);
//element1.addEventListener('click',doSomething2,true);
//element2.addEventListener('click',doSomething,false);
function doSomething(){
console.log("div2 clicked");
}
From the MDN documentation:
useCapture Optional
If true, useCapture indicates that the user wishes to initiate capture. After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. Events which are bubbling upward through the tree will not trigger a listener designated to use capture. See DOM Level 3 Events for a detailed explanation. If not specified, useCapture defaults to false.
You're setting up the handler for "div_1" such that it does not use capture, and the handler for "div_2" such that it does, though since it's got no children that doesn't matter much. Thus:
A click on the blue part ("div_1") triggers only the handler on that element.
A click on the red part ("div_2") triggers the handler for that element. The event then bubbles up the DOM to "div_1", and that handler is triggered.
i have a doubt in eventlistener concept.What is the difference between bellow two codes
i have doubt in the true/false section.no change happens when i replace the 1st code with the second code in my practice code.
a.addEventListener("click", modifyText, true);
a.addEventListener("click", modifyText, false);
true and false in addEventListener is Boolean that specifies whether the event needs to be captured or not.
Here's the syntax and detail:
object.addEventListener (eventName, function, useCapture);
eventName: String that specifies the name of the event to listen for. This parameter is case sensitive!
function: Represents the event listener function to be called when the event occurs.
When an event occurs, an event object is initialized and passed to the event handler as the first parameter. The type of the event object depends on the current event.
useCapture: Boolean that specifies whether the event needs to be captured or not.
One of the following values:
false -> Register the event handler for the bubbling phase.
true -> Register the event handler for the capturing phase.
Bubbling and Capturing Phases:
bubbling: the event object propagates through the target's ancestors in reverse order, starting with the target's parent and ending with the defaultView. This phase is also known as the bubbling phase. Event listeners registered for this phase must handle the event after it has reached its target.
capturing: the event object must propagate through the target's ancestors from the defaultView to the target's parent. This phase is also known as the capturing phase. Event listeners registered for this phase must handle the event before it reaches its target.
For more detail on event flow: DOM Event Architecture
Just have a look at some docu, e.g. MDN on addEventListener:
target.addEventListener(type, listener[, useCapture]);
useCapture
If true, useCapture indicates that the user wishes to initiate capture. After initiating capture, all events of the specified type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree. Events which are bubbling upward through the tree will not trigger a listener designated to use capture. See DOM Level 3 Events for a detailed explanation. Note that this parameter is not optional in all browser versions. If not specified, useCapture is false.
So basically it decides whether the event is handled in the capture or the bubble phase of event processing.
As long as no of the element's parents (or children) have any similar events attached, there is no real difference.