I have two elements, like this:
var parent = document.getElementById("el");
var child = parent.children[0];
and then a eventListener:
parent.addEventListener("click", function() {
alert("Hello World!")
});
and the problem is that when you click the child, the parents click event will be fired.
I want only when you click on the parent to fire the alert, not when you click on the child.
I am not using JQuery and won't.
I have searched on google, and all results uses e.stopPropogation which is JQuery.
So what's the best way to do this in plain JS?
Thanks alot.
You need to prevent event bubbling up to the parent element. For this you have to bind one more event handler to the child element:
child.addEventListener("click", function(e) {
e.stopPropagation();
});
Click event bubbles which means that event travels up DOM tree from child to parent unless its propagation is stopped.
You can check the ID of the clicked element and use that to control the trigger:
http://jsfiddle.net/nccjgtp6/
<div id="el">
This is a thing.
<div id="el2"> This is a second thing. </div>
</div>
var parent = document.getElementById("el");
var child = parent.children[0];
parent.addEventListener("click", function(e) {
console.log(e);
if(e.srcElement.id == 'el') {
alert('Hello world');
}
});
I do not know if this will work consistently in all browsers, but works for me in Chrome.
If I'm not mistaken, it's the event.stopPropagation(); line of code you need.
Related
Consider the following code as an example:
<div id="parent">
<div id="child">info goes here</div>
</div>
//javascript
function something{
//do something;}
//initial attempt
document.getElementById('parent').addEventListener('click',something);
//event capture
document.getElementById('parent').addEventListener('click',something,true);
When I click on the parent element I would like it to do something, and when I click the child I want it to do nothing. The problem is that when I click in the child element it is triggering 'something'.
I thought that it may be event bubbling, such that if I clicked on the child element, the event would bubble up from there to the parent. So then I was thinking about an event capture, but that also causes this problem.
Any advice or suggestions on how to accomplish this would be greatly appreciated.
Instead, check if the element originating the event Event.target - was indeed the desired element.
PS: Don't confuse with Event.currentTarget which (in contrast) is always the Element who has the Event handler attached.
function something (evt){
if (evt.target !== this) return; // Do nothing
// else...
console.log(`#${this.id} clicked`);
}
const el_parent = document.getElementById('parent');
el_parent.addEventListener('click', something);
// Example why you should not use `Event.stopPropagation()`...
document.body.addEventListener('click', () => console.log("BODY is notified!"));
<div id="parent">
PARENT ELEMENT
<div id="child">CHILD ELEMENT</div>
</div>
Don't use Event.stopPropagation()
Event.stopPropagation() would be an idea, but a bad one, since we should avoid an application to (at some layer) prevent an event to bubble - and eventually notify other elements that such an event happened. Imagine your body listens for click events to close custom select dropdowns... If you have elements wandering around your app, and that use Event.stopPropagation() - clicking on such element an opened dropdown will not close - resulting in broken UI. And this was just a simple example.
Use event.stopPropagation to stop event bubbling:
function something() {
console.log("something");
}
document.getElementById('parent').addEventListener('click', something);
document.getElementById('child').addEventListener('click', e => e.stopPropagation());
<div id="parent">
Parent info goes here!
<div id="child">Child info goes here!</div>
</div>
It is event bubbling. Just because you are handling the click event on the child, does not mean it stops bubbling to the parent.
There are two approaches to this. The first one is to stop the event from propagating like this:
document.getElementById('child').addEventListener('click',(e) => { e.stopPropagation(); something() },true);
The second is to check the event target and only run something when the deepest element that caused the click event is the child element:
document.getElementById('child').addEventListener('click',(e) => { e.target.id == "child" ? something() : nothing() },true);
So, i wondered, why this code doesn't work properly, and what can i do, to prevent such a behaviour:
If I would need to prevent event propagation of parent, whilst particular child got clicked, i used method 1, but it seems not to be working, but method 2 is working fine though.
//method 1
$(document).on({
click: function(e) {
console.log('clicked!');
e.preventDefault();
return false;
}
}, '.hax');
//method 2
/*$('.hax').on('click', function(e){
e.preventDefault();
return false;
});*/
//uncommenting will prevent event propagation
.hax {
background-color: whitesmoke;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='wrapper' onclick='alert("hello")'>
<div class='hax'>hax!</div>
</div>
Method 1 Is using event delegation ,so in it event is not directly bind with the element , its bound with the parent ,So in your case the parent is document . in this the case whatever event will be fired for that particular element it will be tracked down from the DOM tree and will execute the parent call before. In your case it will first call the alert from parent .
In method 2 - event is directly bound with the element , The event of parent will still got fired unless you prevent that in the handler but since the handler is bound to the target , you will not face any other action(alert in your case)
Get better Idea of
Event Delegation
You are creating an event delegation by method 1, which can be created the following way too:
$(document).on('click', '.hax', function (e) {
console.log('clicked!');
e.preventDefault();
return false;
});
For clarifying event delegation briefly:
Understanding how events propagate is an important factor in being able to leverage Event Delegation. Any time one of our anchor tags is clicked, a click event is fired for that anchor, and then bubbles up the DOM tree(Up to DOM top), triggering each of its parent click event handlers.
It does not mean you can't achieve your goal here with this method, but in order to make it work, you can create a middle parent for div.hax which is descendant of div.wrapper. I mean:
<div class='wrapper' onclick='alert("hello")'>
<div id="stopHere">
<div class='hax'>hax!</div>
</div>
</div>
Now, we can use method 1, but we only need to stop event propagation / event delegation before it reach div.wrapper. Thus in our newly added div#stopHere:
$("div#stopHere").on('click', '.hax', function (e) {
console.log('clicked!');
e.preventDefault();
return false;
});
I have a Marionette ItemView which is listening for a click on an li; all click events for ItemViews in the ui below that are being ignored for the click handler on the parent li.
I have found a workaround that involves calling e.stopPropagation but it seems the actual solution may be to target the anchor tag rather than the li.
Some sample code:
var LiView = Marionette.ItemView.extend({
events : {
'click' : function(e){
console.log(e.target);
}
}
});
var AnchorView = Marionette.ItemView.extend({
events : {
'click' : function(e){
e.preventDefault();
console.log(e.target);
}
}
});
new LiView({el: 'li'});
new AnchorView({el: 'li a'});
Without stopPropagation the li click event still fires - just as it would when you have multiple jQuery click handlers. The problem is that e.target will refer to the anchor in both the click handlers - something that is undesirable.
Any thoughts on a better way to structure this?
I would just use e.stopPropagation since this is how event bubbling is supposed to work and you're wanting to prevent it from bubbling up.
Since you're binding to click in your LiView there isn't a way to get around that being called unless you bind to something specific in the li that is not the a or parent of the a (as bubbling would still be a problem here).
The alternative (which is messy in my opinion) is to check inside of the LiView click handler against e.target and e.currentTarget to make sure they are the same (if the li itself is what you want clicked unless other elements bubbling up is desirable):
events: {
'click' : function(e) {
if(e.target == e.currentTarget) {
// the person actually clicked on the LI and not something inside of it.
}
}
}
i got a problem
<div id='parent'>
<div id='child'>
</div>
</div>
what i want is when the child is clicked addClass,and when the rest of parent is clicked removeClass,so when i try to do
$('#child').click(function(){
$(this).addClass();
})
$('#parent').click(function(){
$('#child').removeClass();
})
its not working i think its because the child is actually inside the parent,so when the child is clicked the parent clicked right?
so how can i do that?
try this:
$('#child').click(function(evt){
evt.stopPropagation();
$(this).addClass("myClass");
});
You could use event.stopPropagation to prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event.
$('#child').click(function(e){
e.stopPropagation();
$(this).addClass();
});
Several users have already suggested a good solution - here's an explanation of why it works:
When you click an HTML element (actually a DOM object...), the click event "bubbles" all the way up to the root element. For example, a click in #child also triggers a click in #parent, as you expected.
To stop this behavior, you need to call .stopPropagation() on the click event - that will tell the browser that you do not want the event to propagate, but keep it "local". Basically, when you've handled it here, you're done with it and don't want to see it again.
Conveniently, jQuery event handlers take the event as the first argument, so if you assign any function with the signature function (e) { ... }, you can stop event propagation by e.stopPropagation(); as others have suggested. In your case, you want
$('#child').click(function(e){
$(this).addClass();
e.stopPropagation();
});
$('#parent').click(function(){
$('#child').removeClass();
});
How to get DOM or jQuery element by mouse click?
For example I have the next code:
$("*").mouseup(
function()
{
var clickedElement = ???????????????
}
);
So, how to init 'clickedElement' variable?
Thank you.
Inside an event handler, this refers to the DOM element the event was raised on.
$("*").mouseup(function() {
var clickedElement = this;
});
To get a jQuery element, just pass this to jQuery.
But: It would be much much much better to use event delegation, instead of binding an event handler to every element. You can get the origin of the event with event.target:
$(document).mouseup(function(event) {
var clickedElement = event.target;
});
And a further note: This will not necessarily give you the clicked element, but the element over which the mouse button was released, which can be different from the element the button was pressed. You might want to bind to the click event.
I also suggest to read a jQuery tutorial where these basics are covered.
If you don't want to attach an event to every DOM element (which I wouldn't recommend)...
$(document).mouseup(function(event) {
var clickedElement = event.target;
});
jsFiddle.
Here, any element's event will bubble up all the way to document, where it will be handled, and the originating element that started the event will be in event.target.
Use this. i.e.:
$("*").mouseup( function() {
var clickedElement = this; //HTML DOM Element
var $clickedElement = $(this); //jQuery Wrapped HTML DOM Element
} );