Parent Div Click Function gets called when Child Div Click is clicked.
How to overcome this issue
I am using ASP.Net with C#
<div onclick="callme();">
Hi
</div>
When I click on anchor tag the div elements onclick also gets executed. How to overcome this ?
That, of course, depends on the browser.
In newer browsers, call
event.stopPropagation();
on the event object.
In older IE, set
window.event.cancelBubble = true
Read abot the details at Quirks mode.
This is due to event bubbling.
<div onclick="callme();">
Hi
</div>
make sure Callme2() returns false to stop the event from bubbling up.
I tested it out and it seems the inner event fires first. So the inner event could set some kind of flag. Then the outer event looks at that flag: If it is set, the outer event clears it and returns. If not, it does whatever it normally does. Not as fancy as the other answers that have been posted, but I think it would work and should be browser independent, provided all browsers fire the inner event first (which I would guess they do based on the "bubbling" discussion posted here)
Related
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
NOTICE: The cause of the problem has been found, read the comments to the first answer.
I have a dropdown list of things, that is hidden until the user invokes it.
It's something like this:
<div>
<button></button>
<ul>
<li></li>
....
<li></li>
</ul>
</div>
The basic idea:
The list becomes visible when the user presses the button shown in the code above.
I need to make the list able to be navigated by keyboard,
i.e. if the user presses up or down while the list is open, the appropriate li will be selected (as if the mouse was hovering over it instead)
The event listener responsible for giving this functionality to the list should be attached when the list becomes visible and be removed when the list becomes hidden again.
Something like what Bitbucket does for the dropdown lists, but even simpler.
The issue:
I tried to attach an event listener to the ul and then on the div element, when the former had no effect, to no avail.
The code is this
ON SHOW
this.<ul or div element here>.addEventListener('keydown', this.keyboardNavigation.bind(this));
ON HIDE
this.<ul or div element here>.removeEventListener('keydown', this.keyboardNavigation.bind(this));
and the callback is like so
function keyboardNavigation(e) {
console.log('foo');
}
NOTE: "this" is an object to which the div and the ul are both properties of, and the callback function is actually a method of that object.
QUESTION 1:
Why is the keydown event not working when I attach it to either the ul itself or the parent div?
Anyway, since these did not work, I decided to attach the listener to the document.
ON SHOW
document.addEventListener('keydown', this.keyboardNavigation.bind(this));
ON HIDE
document.removeEventListener('keydown', this.keyboardNavigation.bind(this));
Same callback.
Now, while this works, I noticed that the event listener is not removed from the document.
I later noticed that another keydown event listener I had attached to the document for another task, is also not removed when that task is done, while it should.
QUESTION 2:
Why are the event listeners not removed? I cannot understand what I am doing wrong, I am removing the exact same callback on the exact same event as were those that were added.
Any help will be much appreciated.
NOTE:
I have tried doing it with jQuery's .on() and .off() instead, as suggested here, although I do not want to use jQuery, yet same thing is happening.
My thoughts:
1 Is it because the DIV or UL isn't getting the keyboard events because the don't have focus? Whereas the document is always getting the bubbled events?
To test this, click in the DIV/UL and type and see if the keyboard events get triggered then.
I think binding to the document - if you want the user to be able to just start typing after clicking - is the right thing to do here.
2 Is this because you are not removing the same handler you created? You should retain a reference to the handler you create with the first bind call and pass this reference in to the remove call - otherwise you're creating another (different) handler and asking to remove that.
E.g.:
var f = this.keyboardNavigation.bind(this);
document.addEventListener('keydown', f);
document.removeEventListener('keydown', f);
I have designed a website with a menu that is initially invisible. When the user clicks on a button, the menu becomes visible. There are two ways for the user to hide the now visible menu:
Click the button that caused the menu to become visible
Click anywhere on the web page that isn't the menu
The way I have coded the second option is to tie an onclick event to the window element, and have it compare where the user clicked to the menu's position to determine if the menu should be hidden. This works great in Firefox and Safari, but it fails in Mobile Safari.
I noticed that the window onclick event only fires when I click on another element with an onclick event already assigned. If I click on an element with no event(s) assigned, the window's onclick event never fires. If I click on the button which displays the menu, it fires along with the event tied to the button.
Is it possible to assign events to the window element in Mobile Safari?
I'v been encountering this same problem. Here is what worked for me. (Note: I am working within a Modernizr and jQuery context)
First, I add a custom Modernizr class using Modernizr's addTest Plugin API to test for iOS, which will add the class appleios or no-appleios accordingly.
Because in my research the body seems to fire events on it's own agenda, I am taking a little precaution by wrapping all the document's content with an element in an iOS context. Then I add an event handler to this element.
$(".appleios body").wrapInner('<div id="appleios-helper" />');
$("#appleios-helper").bind("mouseup", function(){return;});
What was suggested earlier in this thread is using void(0). I did some quick testing, and found that void(0) as the event just wasn't causing touches on the body to be recognized. When I plugged in my own "empty" function in the form of function(){return;} things started working.
This all hinges on the fact that no events are fired in Mobile Safari unless the element explicitly has events to fire (Safari Web Content Guide.) By inserting this empty event on the wrapper, things will bubble up to the body.
If you're doing strait JavaScript with none of these libraries, the same effect could be achieved in the HTML markup
<html>
...
<body>
<div id="appleios-helper" onmouseup="function(){return;}">
...
</div>
</body>
</html>
This worked for me to hide tooltips when touching anywhere on the document's body. Your mileage may vary.
Simply adding the dummy onclick handler to the html body works for me:
<body onclick="void(0)">
Note that I am using usual live event handlers as shown below:
function liveHandler( event ) {
var target = event.target; ...}
window.addEventListener(evtype, liveHandler, true);
// evtype such as 'mousedown' or 'click'
// we use the capturing mode here (third parameter true)
This is an old question, but I struggled with the same thing today.
I found that using touchstart event works.
I solved it like this:
var isTouchDevice = 'ontouchstart' in document.documentElement;
if (isTouchDevice) {
// Do touch related stuff
$(document).on('touchstart', function (event) {
// Do stuff
});
} else {
// Do non-touch related stuff
$(document).on('click', function () {
// Do stuff
});
}
You could just add onclick="void(0);" to some <div> that covers the whole page so that no matter what, you are always clicking on an element that has an onclick event. Not a great solution, though.
I'd prefer not having the onclick event be tied to the window. Why don't you create a container <div> that has that event on it. Then handle it just like you currently are.
You can also:
$('body').css('cursor', 'pointer');
No idea what those "engineers" at Apple are doing. LOL.
This has problems though. You wouldn't want to do this on every touch device. Only touch devices that don't also have a pointing device (Laptops with Touch Screens, for example).
Source: http://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
The conclusion of the article is this:
So I don’t understand why all this is the case, but it most certainly is the case. If you’re having bubbling problems, just add an empty-function event handler anywhere between the body and the element, and you’re set to go. But it shouldn’t be necessary.
I have HTML similar to the following in my page
<div id="someDiv">
<img src="foo.gif" class="someImg" />
</div>
The wrapper div is set up such that when it is clicked, it's background-color changes using the following jQuery code.
$("div").click(function(event){
$(this).css("background-color", "blue");
});
I also have some jQuery associated with my img that will do some other function (for the sake of argument I am going to display and alert box) like so:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
});
The issue I have come across is that when I click on the img, the event associated with the div is also triggered. I'm pretty sure that this is due to the way that jQuery (or indeed JavaScript) is handling the two DOM elements - clicking the img would require you to also technically click the div, thus triggering both events.
Two questions then really:
Is my understanding of the
DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
Are there any jQuery methods that
would allow me to perform actions on
a child element without invoking
those associated with its parent?
That is known as event bubbling, you can prevent it with stopPropagation():
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
.
Is my understanding of the DOM/JavaScript flawed in some way or
is this actually how things are
occurring?
That is because of what is known event bubbling.
Are there any jQuery methods that would allow me to perform actions
on a child element without invoking
those associated with its parent?
Yes, you need stopPropagation()
No, this is by design. Events bubble up through the entire dom, if you put another handler on body, it would fire too
Yes :) JQuery normalizes the event object, so adding event.stopPropagation() in your img click handler will give you the behavior you expect on all browsers
The problem you just facing is called "event bubbling". That means, if you click on a nested
element, that click event will "bubble up" the DOM tree.
If other elements also are bound to an click event, their listeners will fire aswell.
Solution to prevent this is called:
stopPropagation()
which is used within your event handler
$("img[class=someImg]").click(function(event){
event.stopPropagation();
alert("Image clicked");
});
This is what's called event bubbling, and you can stop it to get the behavior you want with .stopPropagation() (or return false; if you want to stop the event completely, including handlers on the same level), like this:
$("img[class=someImg]").click(function(event){
alert("Image clicked");
event.stopPropagation();
});
You can view a demo here, comment it out and click run again to see the difference.
The short version is that when most event types happen, they happen on the immediate element, then bubble up the DOM, occurring on each parent as they go. This is not jQuery specific at all, native JavaScript does this. If you're more curious, I'd read the linked article, it has a great explanation of what's going on.
Heres my link:
http://tinyurl.com/6j727e
If you click on the link in test.php, it opens in a modal box which is using the jquery 'facebox' script.
I'm trying to act upon a click event in this box, and if you view source of test.php you'll see where I'm trying to loacte the link within the modal box.
$('#facebox .hero-link').click(alert('click!'));
However, it doesn't detect a click and oddly enough the click event runs when the page loads.
The close button DOES however have a click event built in that closes the box, and I suspect my home-grown click event is being prevented somehow, but I can't figure it out.
Can anyone help? Typically its the very last part of a project and its holding me up, as is always the way ;)
First, the reason you're getting the alert on document load is because the #click method takes a function as an argument. Instead, you passed it the return value of alert, which immediately shows the alert dialog and returns null.
The reason the event binding isn't working is because at the time of document load, #facebox .hero-link does not yet exist. I think you have two options that will help you fix this.
Option 1) Bind the click event only after the facebox is revealed. Something like:
$(document).bind('reveal.facebox', function() {
$('#facebox .hero-link').click(function() { alert('click!'); });
});
Option 2) Look into using the jQuery Live Query Plugin
Live Query utilizes the power of jQuery selectors by binding events or firing callbacks for matched elements auto-magically, even after the page has been loaded and the DOM updated.
jQuery Live Query will automatically bind the click event when it recognizes that Facebox modified the DOM. You should then only need to write this:
$('#facebox .hero-link').click(function() { alert('click!'); });
Alternatively use event delegation
This basically hooks events to containers rather than every element and queries the event.target in the container event.
It has multiple benefits in that you reduce the code noise (no need to rebind) it also is easier on browser memory (less events bound in the dom)
Quick example here
jQuery plugin for easy event delegation
P.S event delegation is pencilled to be in the next release (1.3) coming very soon.