I'm trying to put a listener on every tag "a". Here the example: http://fiddle.jshell.net/w5unvaxt/
function callback(e) {
var e = window.e || e;
/*
if (e.target.tagName !== 'A')
return;
*/
alert('The link is: ' + e.target.href);
}
if (document.addEventListener){
document.addEventListener('click', function (event) {
event.preventDefault();
callback(event);
});
}else{
document.attachEvent('onclick', function (event) {
event.preventDefault();
callback(event);
});
}
<!-- Works -->
my text
<!-- not works -->
<a href="http://www.example.com">
<div>my div</div>
</a>
The first example works good but not the second. How can I solve this issue?
The reason your code doesn't work is because the target element being clicked is the div not the a. This means that e.target.href is undefined for the div.
Using event delegation in plain JS is quite difficult. Just looking at the source code of a small library that accomplishes kinda what you want, I can see that it loops through the targets and if the target does not match the specified it assigns target to target.parentNode.
I would recommend using a small library like the ones I have linked (or event jQuery!).
You could use event delegation.
Event delegation allows you to avoid adding event listeners to specific nodes; instead, the event listener is added to one parent. That event listener analyzes bubbled events to find a match on child elements.
Here is an article on how event delegation works.
And the following is my example of how to use event delegation in plain JS.
// Get the parent DIV, add click listener...
document.body.addEventListener("click", function(e) {
// e.target was the clicked element
if(e.target && e.target.nodeName == "A") {
alert(e.target.innerText);
}
});
Link text
This way you only have to attach on event listener to the parent.
Related
I want to be able to activate an element's listener without activating the listener of the div that contains my element.
$('body').on('click', '.thiscoll', function(){
if (type === "form") {
hidePanels();
$('#navbar-pannel').show();
}
});
$('#main_container').on('click', 'a', function(){
hidePanels();
$('#custom-nav').show();
$('#l-name').html("New link name");
$('#l-destination').html("New link destination");
});
The first listener is on my div, while the second listener is on my links that are contained into my div. When I click on a link, it first triggers the 'a' listener, then the '.thiscoll' listener, while I only want to trigger the 'a' listener.
Is it possible?
Thanks.
Long story short, you want to stop event propagation. Something like
$('a').on('click', function(event) {
event.stopPropagation();
// and possibly do something else you require
});
should do.
Yes, what you want is possible. With events in Javascript we have this nice thing called event capturing and event bubbling. By default, browsers will register events in the bubbling phase.
For you, this means that the target you click will have its event handlers fired first. Then its parent's. Then its parent's parent's and so on. You can read more about it on MDN
To stop this propagation, you can use the stopPropagtion method on the Event-object. The Event object is supplied as the first argument in your event listener:
const main = document.querySelector('.main');
const button = document.querySelector('.button');
const stopPropagation = document.getElementById('stopPropagation');
main.addEventListener('click', () => console.log('Clicked on main'));
button.addEventListener('click', (evt) => {
if (stopPropagation.checked) {
evt.stopPropagation();
}
console.log('clicked button');
});
<input id="stopPropagation" type="checkbox">
<label for="stopPropagation">stopPropagation</label>
<div class="main">
Hello, World
<button class="button">Button</button>
</div>
I've been looking for this but I haven't found an answer.
I was wondering which is a better practice in Javascript. To add an event listener that will only work if target is the desired one:
document.addEventListener('click', function(e){
if(e.target && e.target.classList.contains('foo')){
}
});
or to create a variable referring to the desired element and if this exists add the event listener
var a = document.querySelector('.foo');
if (a) {
a.addEventListener('click', function(){
})
}
I always choose the first aproach, because it affects dynamically created elements also. I also think you don't need the first part of the condition. My code looks like this:
document.addEventListener('click', function(e){
if(e.target.id == "confirm-button"){
// do something
}
});
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.
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 have a div, I want to set it so that when I click on something else, it would hide the div.
So I did
$('body').click(function(){
if(loginOpened)
{
$('#loginWindow').animate({
'width':'0px',
'height':'0px'
},"fast");
}
loginOpened=false;
});
However, even when I click in the div itself the event is fired, is there anyway to prevent this?
You can stop it using
e.stopPropagation(); if there is a click event bound to the <div /> tag.
See event.stopPropagation()
Prevents the event from bubbling up
the DOM tree, preventing any parent
handlers from being notified of the
event.
Otherwise you can check the target of the event inside the body click. Check whether event.target is the same as your div.
See event.target
Just check the event.target. If the element that triggered the event is your div do not execute the code.
$('body').click(function(evt){
evt = evt || window.event
if ($(evt.target) != $('#loginWindow')) {
if(loginOpened)
{
$('#loginWindow').animate({
'width':'0px',
'height':'0px'
},"fast");
}
loginOpened=false;
}
});
Yes, but of course Microsoft and the rest of the world came to different conclusions about how to do it. This site gives a good clear rundown of what's needed: http://www.quirksmode.org/js/events_order.html .
I don't use jQuery but the jQuery way appears to be event.stopImmediatePropagation(); as seen in this question: jQuery Multiple Event Handlers - How to Cancel? .
A couple of changes from John's code:
$('body').click(function(ev){
// jQuery means never having to say "window.event"!
// Also, code's cleaner and faster if you don't branch,
// and choose simple breaks over more complex ones
if(!loginOpened) return;
// Lastly, compare using the DOM element;
// jQuery objects never compare as the "same"*
if (ev.target == $('#loginWindow').get(0)) return;
$('#loginWindow').animate({
'width':'0px',
'height':'0px'
},"fast");
loginOpened=false;
});
If trapping it in the body event doesn't work for you, you can just add a simple event handler to the div:
$('#loginWindow').click(function (ev) { ev.stopPropagation(); });
I was going to say return false, but that would prevent other things from firing off the div. stopPropagation just keeps the event from bubbling outward.
I could be really picky, of course...
//Delegation via the document element permits you to bind the event before
// the DOM is complete; no flashes of unbehaviored content
$(document).delegate('body', 'click', function(ev){
//You only have one instance of an id per page, right?
if(!loginOpened || ev.target.id == 'loginWindow') return;
//quotes and px? not necessary. This isn't json, and jQ's smart
$('#loginWindow').animate({width:0,height:0},"fast");
loginOpened=false;
});
* Don't believe me? Try:
jQuery('#notify-container') == jQuery('#notify-container')
Then try
jQuery('#notify-container').get(0) == jQuery('#notify-container').get(0)