stopPropagation breaks event handler - javascript

I have a bit of a head scratcher when it comes to using stopPropagation in javascript. According to many different sources stopPropagation should stop bubbling of the event to parent elements, however, when I use it it seems to stop the event from being called after the first click. I have worked up a very simple bit of code to reproduce the behaviour below:
HTML:
<div id="root">
<div id="top">
<h1>Click Me!</h1>
</div>
</div>
js/jQuery:
var myEvent = document.createEvent("Event");
myEvent.initEvent("boop", true, true);
$("#root").on('boop', function (e) {
alert("root boop!");
});
$("#top").on('boop', function (e) {
// After this is called, this event handler will never fire again.
e.stopPropagation();
alert("top boop!");
});
$("h1").click(function (e) {
$("#top").get(0).dispatchEvent(myEvent);
// I know that $("#top").trigger will prevent the problem, what is wrong with the form above?
});
There is a Fiddle as well.

You dispatch myEvent on which you eventually call .stopPropagation(). Every click thereafter use the same instance of myEvent on which the propagation has been stopped.
You'll need to make a copy of the event before dispatching it if you want to be able to click multiple times.
...or you could rewrite your JavaScript like this:
$("#root").on('boop', function (e) {
alert("root boop!");
});
$("#top").on('boop', function (e) {
e.stopPropagation();
alert("top boop!");
});
$("h1").click(function (e) {
var myEvent = document.createEvent("Event");
myEvent.initEvent("boop", true, true);
$("#top").get(0).dispatchEvent(myEvent);
});
Working JS Fiddle

Related

jQuery calling on() on dynamically generated it after calling click()

I am trying to develop some code to allow the user show/hide a block level element by clicking a button.
The HTML structure is like below
<div class="chat_container"><a class="crm" href="https://google.com" target="_blank">Chat?</a><button id="close_chat"><</button></div>
I have written a click() function for #close_chat which amongst other things changes the ID of the button to #open_chat. I then use the on() method on #open_chat to modify some classes and ids on various elements. In isolation both these methods work, however when combined they don't work. I have noticed that when I click #close_chat even though the ID changes to #open_chat the original event is still attached to the button. After doing some search I suspected the issue might have been related to events bubbling up, but now I am not so sure, still I added event.stopPopagation() to my click function and I can see it appears to be called correctly. I have also tried using the one() method, this appeared to get closer to the behavior I was expecting at the DOM level but still didn't working
My expected behavior is the click() function is called when the user clicks #close_chat, the event is then unbound allowing the .on() event to be called on #open_chat. Id than of course have to reset the original functionality. My code looks like this
$(document).ready(function () {
var close = "<button id='close_chat'><</div>";
var container = $("<div />");
container.addClass("chat_container");
var crmChat = $("<a />");
crmChat.addClass("crm");
crmChat.attr("href", "https://google.com");
crmChat.attr("target", "_blank");
crmChat.text("Chat?");
console.log(crmChat);
console.log(container);
$(container).insertAfter("#heading");
$(container).prepend(crmChat);
$(close).insertAfter(crmChat);
$("#close_chat").click(function (event) {
$("#close_chat").removeAttr("id").attr("id", "open_chat");
event.stopPropagation();
alert(event.isPropagationStopped());
//return false;
});
$(".chat_container").on("#open_chat", "button", function () {
//$(".crm_chat_container").addClass("animate-open").removeClass("animate-close");
$("#open_chat").html(">").removeAttr("id").attr("id", "reopen");
//event.stopPropagation();
});
});
any help is greatly appreciated
Sam
edit, I have now updated my code to look like so
//onclick function for our close button
$("#close_chat").click(function (event) {
attachClosedChatListner();
});
function attachOpendChatListener() {
$(".chat_container").on("click","#open_chat", function () {
$("#open_chat").removeAttr("id").attr("id", "close_chat");
$("#close_chat").html("<")
$(".crm_chat_container").removeClass("animate-close").addClass("animate-open");
});
//attachClosedChatListner();
}
function attachClosedChatListner() {
$("#close_chat").off('click');
$("#close_chat").removeAttr("id").attr("id", "open_chat");
$("#open_chat").html(">")
$(".chat_container").removeClass("animate-open").addClass("animate-close");
//attachOpendChatListener();
}
What about re-attaching the event?
$("#close_chat").click(function (event) {
$("#close_chat").removeAttr("id").attr("id", "open_chat");
attachOpenChatListener();
event.stopPropagation();
alert(event.isPropagationStopped());
//return false;
});
function attachOpenChatListener() {
$("#close_chat").off('click');
$(".chat_container").on("#open_chat", "button", function () {
//$(".crm_chat_container").addClass("animate-open").removeClass("animate-close");
$("#open_chat").html(">").removeAttr("id").attr("id", "reopen");
//event.stopPropagation();
});
}
I managed to work this out, the click function was causing the problem
//onclick function for our close button
$("#close_chat").click(function (event) {
attachClosedChatListner();
});
I've replaced it with .on and it works now
$(".crm_chat_container").on("click", "#close_chat", function (event) {
$("#close_chat").off('click');
$("#close_chat").removeAttr("id").attr("id", "open_chat");
$("#open_chat").html(">");
$(".crm_chat_container").removeClass("primo-animate-open").addClass("animate- close");
attachCloseChatListener();
event.stopImmediatePropagation();
});
function attachCloseChatListener() {
$(".crm_chat_container").on("click", "#open_chat", function (event) {
$("#open_chat").off('click');
$(".crm_chat_container").removeClass("primo-animate-close").addClass("primo-animate-open");
$("#open_chat").removeAttr("id").attr("id", "close_chat");
$("#close_chat").html("<");
event.stopImmediatePropagation();
});
}
on thing is my click events appears to be firing multiple times, that is after clicking my buttons a few times I see several click events in dev tools.
Anyway, thanks for putting me on the right path

Actually do default after preventing default

I want to prevent default behavior of a link (a) then do the default behavior, let's say open open a link in a new window.
Here is some HTML code:
<a href="somewhere" target="_blank" id="mylink">
And the JS code:
document.getElementById('mylink').addEventListener('click', function (e) {
e.preventDefault();
axios.post('options', new FormData(document.querySelector('#myform')))
.then(function(){
// Here I want to do what the link should have done!
});
});
I know I can do something like this:
window.open(e.target.href);
But it's not an option because the browser consider this as a popup. And I don't want to rewrite something in JS, just consider the link as usual: this link has to do its default behavior (which was prevented).
Is there a way to do this?
Here is some idea:
var openingPopup = false;
document.getElementById('mylink').addEventListener('click', function (e) {
if(!openingPopup){
e.preventDefault();
axios.post('options', new FormData(document.querySelector('#myform')))
.then(function(){
// make sure this would not run twice
openingPopup = true;
document.getElementById('mylink').click();
});
}else{
// skipping this only for one time
openingPopup = false;
}
});
This way,
you run the popup opener click handler once,
then prevent the others,
trigger the click event again manually,
this time do nothing, but allow others to run.
As per #Electrox-Qui-Mortem 's suggested link, after you complete your pre-process(es), you can remove the event listener and call the click again.
(relevant MDN link: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener )
const anchor = document.getElementById('anchor');
anchor.addEventListener('click', test);
function test(e){
e.preventDefault();
alert('test');
if( true ){
console.log( this );
this.removeEventListener('click', test);
this.click();
}
}
<a id="anchor" href="https://www.google.com">Test</a>
It's got wonky performance inside code testing tools (like JSfiddle and CodePen) -- you would have to test it in your actual application to make sure if it works appropriately for your use case.
I've only had good results with removeEventListener when referencing an outside function as the "listener" func. In this case test()
In a more "native" way you could create a new "click" event which is not cancelable and trigger it against the same element.
var anchor = document.querySelector('#target');
function triggerClick(target) {
var newClick = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: false
});
target.dispatchEvent(newClick);
}
anchor.addEventListener('click', function(e) {
e.preventDefault();
if(e.defaultPrevented) {
alert('Prevented!');
triggerClick(anchor);
}
else {
alert('Not prevented');
}
});
Click me!
Here the key is the cancelable: false in the new event created in the function triggerClick(target), which bypass the e.preventDefault().
In the embedded example on StackOverflow it doesn't work, but here's a JSFiddle!

Removing onclick listener for a part of division

I have a button inside a division.Both have separate onclick listeners.But since button is a part of the division,the event attached to button is also triggered when clicked.Is there a way to remove it?
i tried :not / .not.it dint work.
<div id="divson">
<button id="btn"></button>
</div>
$('#divson').not('#btn').click(function sayHello() {
alert('Helo!');
});
$('#btn').click(function sayJello() {
alert('Jelo!');
});
http://jsfiddle.net/gw3LqrcL/
Just return false; in your handler to stop the event propagation: http://jsfiddle.net/gw3LqrcL/1/
Use stopPropagation on the event passed in to the handler on #btn to stop the event bubbling to the parent element:
$('#divson').click(function () {
alert('Helo!');
});
$('#btn').click(function (e) {
e.stopPropagation();
alert('Jelo!');
});
Updated fiddle

Interrupt all mousedown events to simulate like a mouse click

I want to trigger click event on a element when mousedown occurs. Also, I want to enable this feature for all elements in a html page.
Is it possible with jQuery on Chrome ?
Here's my first attempt;
$.fn.mousedown = function (onclick) {
this.bind("click", function (e) { onclick.call(this, e); });
return this;
};
But this mousedown elements fired after click occurs.
$(document).on('mousedown', function (e) { $(e.target).trigger('click') })
I'm not sure though for what this should be useful.
To prevent the second click (which is the normal click) you have to do some extra work
$(document).on('mousedown', function (e) {
$(e.target).trigger('click').once('click', function (e) {
e.preventDefault();
e.stopPropagation();
});
})

JQuery event.stopPropagation() not working

In my html I have a span of class dragHandle embedded within a li.
<div class='treeView'>
<ul class='tree'>
<li><span class="dragHandle"></span>Item 1
<ul>
<li><span class="dragHandle"></span>Item 2 link</li>
</ul>
</li>
</ul>
I attach event handlers using jQuery as follows:
$(".tree li").click(function(event) {
alert("click");
event.stopPropagation();
});
$(".dragHandle").mousedown(function(event) {
alert("down");
event.stopPropagation();
});
$(".dragHandle").mouseup(function(event) {
alert("Up");
event.stopPropagation();
});
When I mousedown and mouse up over the element I get the down and up alerts, however I also get the click alert of the li's event handler too. I thought that this should be prevented from by the call to event.stopPropagation in the mousedown and mouseup handlers. How do I stop the click event being called for mousedown/up events on the dragHandle?
TIA,
Adam
How do I stop the click event being called for mousedown/up events on the dragHandle?
You capture... and eat... that event:
$(".dragHandle").click(function(event) { event.stopPropagation(); });
The key here is that click, mousedown, and mouseup are distinct events. Although you might think of a click as being a mousedown followed by a mouseup, in reality you might have click events triggered by user actions that don't even involve the mouse, as well as combinations of mousedown and mouseup that don't result in any click events at all.
You could create a simple wrapper-"class", that keeps track of mouse-down and up events:
(function () {
var DragDropHandler = {
isDrag: false,
mouseDownHandler: function (event) {
alert("down");
event.stopPropagation();
this.isDrag = true;
},
mouseUpHandler: function (event) {
alert("Up");
event.stopPropagation();
this.isDrag = false;
},
clickHandler: function (event) {
event.stopPropagation();
// Check if we've already received a mouseDown-event. If we have,
// disregard the click event since we want drag-functionality
if(this.isDrag) { return; }
alert("click");
}
};
$(".tree li").click(function(event) {
DragDropHandler.clickHandler.call(DragDropHandler, event);
});
$(".dragHandle").mousedown(function(event) {
DragDropHandler.mouseDownHandler.call(DragDropHandler, event);
});
$(".dragHandle").mouseup(function(event) {
DragDropHandler.mouseUpHandler.call(DragDropHandler, event);
});
})();
This creates a closure and delegates the event handling to the DragDropHandler-object. Note that I've used function.call (the first parameter is the context) to ensure that this refers to the DragDropHandler-object inside its methods. Since we have created an anonymous function that can not be reached from global space, I think it's acceptable to use the DragDropHandler reference inside the wrapper event handlers.

Categories