Bypassing event.stopPropagation - javascript

I'm writing a JavaScript add-on (not in jQuery) which starts working when a certain event has been triggered. It works fine on most of the sites, but I encountered problems in sites that use stopPropagation().
Is it possible to bypass this and attach the event anyway?

For standards-compliant browsers, use the capturing phase. For IE, you can capture mouse events by calling setCapture on an element (probably the body). Once you've done your thing, call fireEvent on the event object's srcElement.

How does stopPropagation() get in your way? (Add more description how/on what your add-on works/attaches).
The only thing stopPropagation() does, is to stop the event from bubbling up to the parent element of the element which received the event. Thus the event handlers on the parent aren't called. But multiple handlers for the same event directly on the element are all called.
So... as long as you bind your event-handler directly to the element which the event is first generated on first, you are ok. If you just try to listen for events on e.g. on body and rely on all events bubbling up to you, you are out of luck.
e.g. if you now click on the red div you will get an alert saying sibling handler and one saying inline handleralthough the earlier defined inline-onclick handler calls stopPropagation().
(Note: this sample doesn't handle IE specifics which uses attachEvent() and cancelBubble)
<style type="text/css" media="screen">
#parent1 { background-color: green; width:300px; height:300px }
#test { background-color: red; width:200px; height:200px }
</style>
<div id="parent1">
<div id="test" onclick="javascript:event.stopPropagation();alert('inline handler');"></div>
</div>
<script type="text/javascript">
parent1.addEventListener('click',
function(e) { alert('parent'); },
false
);
test.addEventListener('click',
function(e) { alert('sibling handler'); },
false
);
</script>

Related

In jQuery, how can I replace the body of my document and still get JavaScript to fire?

Currently, my page has a top navigation bar.
When clicking, the page currently does a full reload which causes a flickering effect.
To avoid this, I'm planning to use the jQuery html function to just replace the main body of the page and leave the header intact.
But this appears to stop JavaScript function from running on the main body of the page.
Is there a way I can use the jQuery html function and still get my JavaScript to work on the HTML elements in the body?
You can use event bubbling to listen to the events on document, and based on the target element, execute appropriate event handling logic.
jQuery makes it easier to register such event handlers by simplifying the logic around identification of the target element. The syntax for registering such an event handler would be: $(document).on(events, selector, handler).
These are called Delegated Event Handlers in jQuery's documentation:
Delegated event handlers have the advantage that they can process events from descendant elements that are added to the document at a later time. By picking an element that is guaranteed to be present at the time the delegated event handler is attached, you can use delegated events to avoid the need to frequently attach and remove event handlers.
I have illustrated an example in the snippet below. Notice how you do not need to re-register the event listener even after replacing the HTML content.
$(document).on("click", "#clickMe", function() {
alert("Element clicked.");
});
// Change the innerHTML after 10 seconds.
setTimeout(function() {
$(document.body).html(`
<div id="clickMe">[New] Click Me</div>
`);
}, 10000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="clickMe">Click me</div>
You can read more about event bubbling here and more about jQuery's event handling syntax here.
Note: In my opinion, you need to rethink your architecture. While the suggested approach works, given the unconventional nature of your architecture, you may run across other challenges in future.

Jquery events (e.g. click) fire twice due to debugger

I am sorry that my origin description is not clear. I have revised my question:
I am using jQuery, as shown in the attached picture, each time I attach an event listener (e.g. click) to an element, it will fire twice. It looks like this problem is due to the jQuery debugger.
I would like to ask if there is any approach other than adding unbind() or off() before the listener to solve the problem, I have tried these approaches but it removes the listeners already attached to the elements
(e.g. I have #element1 which is created in html, then use jQuery to append two elements #element2 and #element3 to #element1, if I use
$('#element1').off('click').on('click', '#element2', function(){
//implementation
}
$('#element1').off('click').on('click', '#element3', function(){
//implementation
}
the click event of #element2 will no longer work because the click listener attached on it is removed in this situation. So I am asking if there is a better way to do it?
By the way, I am wondering where is the VM 7188 listener comes from (it looks like from jQuery debugger) and if there is anyway to remove this listener so that I no longer need to use off() every time when I write a listener, thanks.
Attached picture for duplicated listeners
It's unclear why you'd need to remove and attach a handler to #element1 just because #element2 is removed and dynamically created; you're using delegated syntax, so it's totally fine that #element2 is dynamically created. You only need to set that handler once, regardless. Example:
// Note this continues to work even as we remove and recreate #element2
$("#element1").on("click", "#element2", function() {
console.log("Got the click on #element2");
});
function tick() {
$("#element1").append("<span id='element2'>Click me</span>");
setTimeout(function() {
$("#element2").remove();
setTimeout(tick, 500);
}, 1500);
}
tick();
<div id="element1"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
But to target specific handlers, you can use event namespaces, e.g.:
$('#element1').off("click.my-namespace").on('click.my-namespace', '#element2', function(){
//implementation
});
For more, see "Event names and namespaces" in the documentation.
I'm not a jquery person, but it seems to me that you are adding the handler to a child descendant and therefore the event bubbles. Instead of adding/removing listeners like this, when adding the handler to children, consider adding event.stopPropagation()
$('#element1').unbind().on('click', '#element2', function(e){
e.stopPropagation();//#element1 won't receive this anymore
//implementation
}
You can call inline function, add attr to your element
onclick="handler"
Remove old handler before bind new
$("#element1").off("click", "#element2", handler);
$("#element1").on("click", "#element2", handler);

Why is the onClick event triggered twice?

I have the following html:
<span onclick="alert('Boem')">
<button id="test1">test</button>
</span>
When I call the following javascript:
$('#test1').trigger('click');
The onclick event is triggered twice, while I expect it to just trigger once. Because JQuery should look up in the DOM tree and find only one onclick.
I do not have control of the span, it is generated in a third party tool. I do have control over the button and his parameters.
You can find a JSFiddle example here:
http://jsfiddle.net/Voga/v4100zke/
Update
I do not know the contents of the onclick listener of the span. This is also generated by the 3rd party tool. I want the click trigger to execute this onclick like it does now, but only once.
It is calling twice because button is inside a span and span has onclick="alert('Boem')", hence when you trigger click on button then it shows alert and same click event propagate to span and shows alert once again.
you need to stop default behaviour of button using below code :
$(function(){
$('#test1').click(function(e){e.preventDefault();}).click();
});
Demo
I was also getting a similar issue where I had to download a pdf which was downloading twice. I used jQuery's stopImmediatePropagation method.
$( "#test1" ).on("click", function(e) {
e.stopImmediatePropagation();
});
$('#test1').trigger('click');
stopImmediatePropagation will prevent any parent handlers and also any other handlers from executing. Refer here for more details.
to stop event propagation use :stopPropagation
$( "#test1" ).on( "click", function(ev) {
ev.stopPropagation();
});
$('#test1').trigger('click');
Please note, in order events are assigned in DOM.
DEMO
Bubbling. The click event on the child also happens on the parent (span).
This thread will help answer the 'why' of it.
A better way would be to just assign a click handler to the element so as to keep your Javascript abstracted out of the HTML:
http://jsfiddle.net/v4100zke/3/
$('#test1').click(function() {
alert('Boem')
});
Hope that helps.
This should also work:
$($('#test1').parent()).trigger('click');
Here button click is triggered. Since the button is inside the span and onclick event is defined for span the two alert are happened.
One is by the button
and
other by its parent(parent have onclick event and button click is triggered).
To avoid two alert, use this
<span>
<button onclick="alert('Boem')" id="test1">test</button>
</span>
I had the same problem. Mine was the fact that I was using .trigger('.element') and there were two elements with that element class.
So, I got more specific, and used .trigger('.parent .element') to ensure only one click event happened on a single element, instead of one click event on two elements.
Note that you have used jQuery's click method, it will emit both dom's mouse click event and jquery's click event. Then both jquery's click Event and dom's mouse click event are propagated to the span element and its onclick listener is triggered twice hence it alert twice
check this demo
to figure it out
as for how to deal with it, just use stopPropagation as answers above
$('#test1').click(function() {
// comment this line and run, 'parent' will be alerted TWICE!!! since both mouseEvent and jQuery.Event trigger the onclick listener
$('#children').click(function(e) {e.stopPropagation()})
});
I also face this problem on Modal
Solved by the following tricks -
$('#saveButton').off('click').on('click', { modal: modal }, save)
Here off('click') prevents the save method fires multiple times.
I have tried all the above given advice, but unfortunately non of the above works for me.
"myBtn" is individual div in my case, so using flag and setTimeout I have achieved my desired result. Please advice.
I have tried :
.off()
.unbind()
e.stopImmediatePropagation()
e.preventDefault();
Below is the working code for me, I know its not a good practice, But for now I dont have any solution. Please advice
var isClicked = false;
$("#myBtn").on("click",function(e){
if(!isClicked){
isClicked = true;
console.log("button clicked");
setTimeout(function(){
isClicked = false;
},3000);
}
})
Why is the onClick event triggered twice?
This happens because of bubbling. This is because, as javascript.info says: "events “bubble” from the inner element up through parents like a bubble in the water".
How do I solve it?
If your third party tool generates your <span></span> element with the onclick attribute included, this probably means that your tool wants you to treat your span directly as your button, without the necessity of creating an additional one as you're doing with your <button></button> element.
Here, to fix this error, the best way it's to programatically click the SAME element that contains the handler to avoid propagation:
$('#test1').trigger('click');
/* Now, you can take #test1 and make it look and function as a button */
#test1 {
padding: 10px 20px;
color: white;
background-color: #0078d7;
font-size: 2rem;
font-family: Helvetica;
}
#test1:hover {
cursor: pointer;
background-color: blue;
transition: all 0.25s;
}
<!-- Script to include jQuery v3.3.1 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<span id="test1" onclick="alert('Boem')">test</span>

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

stopPropagation vs. stopImmediatePropagation

What's the difference between event.stopPropagation() and event.stopImmediatePropagation()?
stopPropagation will prevent any parent handlers from being executed stopImmediatePropagation will prevent any parent handlers and also any other handlers from executing
Quick example from the jquery documentation:
$("p").click(function(event) {
event.stopImmediatePropagation();
});
$("p").click(function(event) {
// This function won't be executed
$(this).css("background-color", "#f00");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Note that the order of the event binding is important here!
$("p").click(function(event) {
// This function will now trigger
$(this).css("background-color", "#f00");
});
$("p").click(function(event) {
event.stopImmediatePropagation();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>example</p>
Surprisingly, all other answers only say half the truth or are actually wrong!
e.stopImmediatePropagation() stops any further handler from being called for this event, no exceptions
e.stopPropagation() is similar, but does still call all handlers for this phase on this element if not called already
What phase?
E.g. a click event will always first go all the way down the DOM (called “capture phase”), finally reach the origin of the event (“target phase”) and then bubble up again (“bubble phase”). And with addEventListener() you can register multiple handlers for both capture and bubble phase independently. (Target phase calls handlers of both types on the target without distinguishing.)
And this is what the other answers are incorrect about:
quote: “event.stopPropagation() allows other handlers on the same element to be executed”
correction: if stopped in the capture phase, bubble phase handlers will never be reached, also skipping them on the same element
quote: “event.stopPropagation() [...] is used to stop executions of its corresponding parent handler only”
correction: if propagation is stopped in the capture phase, handlers on any children, including the target aren’t called either, not only parents
...and: if propagation is stopped in the bubble phase, all capture phase handlers have already been called, including those on parents
A fiddle and mozilla.org event phase explanation with demo.
A small example to demonstrate how both these propagation stoppages work.
var state = {
stopPropagation: false,
stopImmediatePropagation: false
};
function handlePropagation(event) {
if (state.stopPropagation) {
event.stopPropagation();
}
if (state.stopImmediatePropagation) {
event.stopImmediatePropagation();
}
}
$("#child").click(function(e) {
handlePropagation(e);
console.log("First event handler on #child");
});
$("#child").click(function(e) {
handlePropagation(e);
console.log("Second event handler on #child");
});
// First this event will fire on the child element, then propogate up and
// fire for the parent element.
$("div").click(function(e) {
handlePropagation(e);
console.log("Event handler on div: #" + this.id);
});
// Enable/disable propogation
$("button").click(function() {
var objectId = this.id;
$(this).toggleClass('active');
state[objectId] = $(this).hasClass('active');
console.log('---------------------');
});
div {
padding: 1em;
}
#parent {
background-color: #CCC;
}
#child {
background-color: #000;
padding: 5em;
}
button {
padding: 1em;
font-size: 1em;
}
.active {
background-color: green;
color: white;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent">
<div id="child"> </div>
</div>
<button id="stopPropagation">Stop Propogation</button>
<button id="stopImmediatePropagation" ">Stop Immediate Propogation</button>
There are three event handlers bound. If we don’t stop any propagation, then there should be four alerts - three on the child div, and one on the parent div.
If we stop the event from propagating, then there will be 3 alerts (all on the inner child div). Since the event won’t propagate up the DOM hierarchy, the parent div won’t see it, and its handler won’t fire.
If we stop propagation immediately, then there will only be 1 alert. Even though there are three event handlers attached to the inner child div, only 1 is executed and any further propagation is killed immediately, even within the same element.
I am a late comer, but maybe I can say this with a specific example:
Say, if you have a <table>, with <tr>, and then <td>. Now, let's say you set 3 event handlers for the <td> element, then if you do event.stopPropagation() in the first event handler you set for <td>, then all event handlers for <td> will still run, but the event just won't propagate to <tr> or <table> (and won't go up and up to <body>, <html>, document, and window).
Now, however, if you use event.stopImmediatePropagation() in your first event handler, then, the other two event handlers for <td> WILL NOT run, and won't propagate up to <tr>, <table> (and won't go up and up to <body>, <html>, document, and window).
Note that it is not just for <td>. For other elements, it will follow the same principle.
event.stopPropagation will prevent handlers on parent elements from running.
Calling event.stopImmediatePropagation will also prevent other handlers on the same element from running.
From the jQuery API:
In addition to keeping any additional
handlers on an element from being
executed, this method also stops the
bubbling by implicitly calling
event.stopPropagation(). To simply
prevent the event from bubbling to
ancestor elements but allow other
event handlers to execute on the same
element, we can use
event.stopPropagation() instead.
Use
event.isImmediatePropagationStopped()
to know whether this method was ever
called (on that event object).
In short: event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running.
1)event.stopPropagation():
=>It is used to stop executions of its corresponding parent handler only.
2) event.stopImmediatePropagation():
=> It is used to stop the execution of its corresponding parent handler and also handler or function attached to itself except the current handler.
=> It also stops all the handler attached to the current element of entire DOM.
Here is the example: Jsfiddle!
Thanks,
-Sahil
Here is a demo to illustrate the difference:
document.querySelectorAll("button")[0].addEventListener('click', e=>{
e.stopPropagation();
alert(1);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
e.stopImmediatePropagation();
alert(1);
});
document.querySelectorAll("button")[0].addEventListener('click', e=>{
alert(2);
});
document.querySelectorAll("button")[1].addEventListener('click', e=>{
alert(2);
});
<div onclick="alert(3)">
<button>1...2</button>
<button>1</button>
</div>
Notice that you can attach multiple event handlers to an event on an element.
event.stopPropagation() allows other handlers on the same element to be executed, while event.stopImmediatePropagation() prevents every event from running. For example, see below jQuery code block.
$("p").click(function(event)
{ event.stopImmediatePropagation();
});
$("p").click(function(event)
{ // This function won't be executed
$(this).css("color", "#fff7e3");
});
If event.stopPropagation was used in previous example, then the next click event on p element which changes the css will fire, but in case event.stopImmediatePropagation(), the next p click event will not fire.
Here I am adding my JSfiddle example for stopPropagation vs stopImmediatePropagation.
JSFIDDLE
let stopProp = document.getElementById('stopPropagation');
let stopImmediate = document.getElementById('stopImmediatebtn');
let defaultbtn = document.getElementById("defalut-btn");
stopProp.addEventListener("click", function(event){
event.stopPropagation();
console.log('stopPropagation..')
})
stopProp.addEventListener("click", function(event){
console.log('AnotherClick')
})
stopImmediate.addEventListener("click", function(event){
event.stopImmediatePropagation();
console.log('stopimmediate')
})
stopImmediate.addEventListener("click", function(event){
console.log('ImmediateStop Another event wont work')
})
defaultbtn.addEventListener("click", function(event){
alert("Default Clik");
})
defaultbtn.addEventListener("click", function(event){
console.log("Second event defined will also work same time...")
})
div{
margin: 10px;
}
<p>
The simple example for event.stopPropagation and stopImmediatePropagation?
Please open console to view the results and click both button.
</p>
<div >
<button id="stopPropagation">
stopPropagation-Button
</button>
</div>
<div id="grand-div">
<div class="new" id="parent-div">
<button id="stopImmediatebtn">
StopImmediate
</button>
</div>
</div>
<div>
<button id="defalut-btn">
Normat Button
</button>
</div>

Categories

Resources