I have a script that slides a div out of view when the user clicks on the background (the <body>).
Here's my code:
// Slide back out of view.
$('body').click(function(){
$('.presentationArea').animate({'height': '0px'}, 1000);
});
But, see, here's the problem; If the user clicks ANYWHERE on the page, I.E. Menu links, buttons, textfields, images, the above function gets called! I only want it to be called if the user clicks the "body", you know, that thing BEHIND everything else? :) How would one do this?
Thank you
I only want it to be called if the user clicks the "body", you know, that thing BEHIND everything else? :)
You can check event.target and see if it's the actual document.body element. (Live example) But I suspect people are going to find it difficult to click that as opposed to (say) a p element, because the body doesn't typically fill the display area unless something is there to expand it (although you can do it with CSS).
But fundamentally, you can use event.target to see where the click was and decide at that point whether it's a click you want to handle (whether it was technically actually on body itself or something else you want to go ahead and treat the same way).
Alternately, you could hook up a handler to stop event bubbling via stopPropagation on all of the elements you don't want clicked — e.g., for a elements:
$('a').click(function(event) {
event.stopPropagation();
});
stopPropagation just stops bubbling, not the default action (e.g., it doesn't keep people from following the link). But the side-effects of that might be a pain (I haven't done it globally like that, only targeted).
You should check whether e.target is the <body> element.
You are experiencing "bubbling" your click will bubble from the child element all the way up to the parent and trigger the function.
See the answer on this thread for more details:
How to stop event bubbling with jquery live?
Basically, you'll have to add a new function that on click has return false to stop the bubbling.
$('#wrapper').bind(void(0), false)
Should work assuming you have a wrapper div, as the second parameter "false" prevents bubbling.
Okay, I've got it:
$("body").click(function(event) {
if(event.target.nodeName == "body" || event.target.nodeName == "BODY")
{
$('.presentationArea').animate({'height': '50px'}, 1000);
}
});
use something like this:
// Slide back out of view.
$('body').click(function(event){
if (event.target === this) {
$('.presentationArea').animate({'height': '0px'}, 1000);
}
});
Related
I'm wondering whether there is an easy way to detect a click on a link that appears within a div on which I want to handle clicks...
So, there is an simple example of HTML code:
<div class="checkmark">
<div class="box"> </div>
<div class="label">Checkbox label possibly with an anchor.</div>
</div>
So in this example, I use a set of <div> tags to create a checkmark. The "box" is where I show a little square and when checked, also show the checkmark (a red cross, for example.)
To make the checkmark work as expected, I use jQuery and capture mouse clicks on the main <div> tag:
jQuery("checkmark").click(function(e){
e.stopPropagation();
e.preventDefault();
jQuery("box", this).toggle("checked");
});
Pretty easy, that works great (the "checked" class is enough to show a checkmark since that can be defined using CSS.)
However, as we can see in the example, the "label" includes an anchor. If I click the anchor, the jQuery I just presented runs, but the anchor does nothing. If I remove the stopPropagation() and preventDefault() the anchor gets clicked, but the checkmark is toggled too.
What I'm wondering is: is there an easy way to check whether the propagation would trigger the anchor and in that case just ignore the click in the "checkmark" code?
Something like that:
jQuery("checkmark").click(function(e){
if(!(anchor.clicked())) // how do we do this?
{
e.stopPropagation();
e.preventDefault();
jQuery("box", this).toggle("checked");
}
});
P.S. I do not know whether there are anchors in the label. So the discovery has to happen in the click() function (unless there is a better setup and that "if" could happen differently).
Note: here I show a target="blank" parameter. In the real deal I will actually open a popup, but that doesn't really make a difference here.
This is what event.target is for.
For example, in this case:
if($(e.target).is("a")) {
// It was the anchor element that was clicked
}
jsFiddle here
You can just add this handler:
jQuery("checkmark a").click(function(e){
e.stopPropagation();
}
It will stop the click event from bubbling from the link to the div, so the link will be activated, and the event never reaches the div where it would be stopped.
You could use the event delegateTarget property to see which DOM element triggered the event.
if($(e.delegateTarget).is("a"))
// execute code
We are working on a JavaScript tool that has older code in it,
so we cannot re-write the whole tool.
Now, a menu was added position fixed to the bottom and the client would very much like it to have a toggle button to open and close the menu,
except closing needs to happen automatically when a user starts doing things out side of the menu, for example, when a user goes back into the page, and selects something or clicks on a form field.
This could technically work with a click event on the body, triggering on any click,
however there are numerous items in the older code, where a click event was handled on an internal link, and return false was added to the click function, in order for the site not to continue to the link's href tag.
So clearly, a general function like this does work, but not when clicked on an internal link where the return false stops the propagation.
$('body').click(function(){
console.log('clicked');
});
Is there a way I can force the body click event anyway,
or is there another way I can let the menu dissappear, using some global click event or anything similar?
Without having to rewrite all other clicks in the application that were created years ago.
That would be a monster task, especially since I have no clue how I would rewrite them, without the return false, but still don't let them go to their href.
Events in modern DOM implementations have two phases, capturing and bubbling. The capturing phase is the first phase, flowing from the defaultView of the document to the event target, followed by the bubbling phase, flowing from the event target back to the defaultView. For more information, see http://www.w3.org/TR/DOM-Level-3-Events/#event-flow.
To handle the capturing phase of an event, you need to set the third argument for addEventListener to true:
document.body.addEventListener('click', fn, true);
Sadly, as Wesley mentioned, the capturing phase of an event cannot be handled reliably, or at all, in older browsers.
One possible solution is to handle the mouseup event instead, since event order for clicks is:
mousedown
mouseup
click
If you can be sure you have no handlers cancelling the mouseup event, then this is one way (and, arguably, a better way) to go. Another thing to note is that many, if not most (if not all), UI menus disappear on mouse down.
In cooperation with Andy E, this is the dark side of the force:
var _old = jQuery.Event.prototype.stopPropagation;
jQuery.Event.prototype.stopPropagation = function() {
this.target.nodeName !== 'SPAN' && _old.apply( this, arguments );
};
Example: http://jsfiddle.net/M4teA/2/
Remember, if all the events were bound via jQuery, you can handle those cases just here. In this example, we just call the original .stopPropagation() if we are not dealing with a <span>.
You cannot prevent the prevent, no.
What you could do is, to rewrite those event handlers manually in-code. This is tricky business, but if you know how to access the stored handler methods, you could work around it. I played around with it a little, and this is my result:
$( document.body ).click(function() {
alert('Hi I am bound to the body!');
});
$( '#bar' ).click(function(e) {
alert('I am the span and I do prevent propagation');
e.stopPropagation();
});
$( '#yay' ).click(function() {
$('span').each(function(i, elem) {
var events = jQuery._data(elem).events,
oldHandler = [ ],
$elem = $( elem );
if( 'click' in events ) {
[].forEach.call( events.click, function( click ) {
oldHandler.push( click.handler );
});
$elem.off( 'click' );
}
if( oldHandler.length ) {
oldHandler.forEach(function( handler ) {
$elem.bind( 'click', (function( h ) {
return function() {
h.apply( this, [{stopPropagation: $.noop}] );
};
}( handler )));
});
}
});
this.disabled = 1;
return false;
});
Example: http://jsfiddle.net/M4teA/
Notice, the above code will only work with jQuery 1.7. If those click events were bound with an earlier jQuery version or "inline", you still can use the code but you would need to access the "old handler" differently.
I know I'm assuming a lot of "perfect world" scenario things here, for instance, that those handles explicitly call .stopPropagation() instead of returning false. So it still might be a useless academic example, but I felt to come out with it :-)
edit: hey, return false; will work just fine, the event objects is accessed in the same way.
this is the key (vs evt.target). See example.
document.body.addEventListener("click", function (evt) {
console.dir(this);
//note evt.target can be a nested element, not the body element, resulting in misfires
console.log(evt.target);
alert("body clicked");
});
<h4>This is a heading.</h4>
<p>this is a paragraph.</p>
If you make sure that this is the first event handler work, something like this might do the trick:
$('*').click(function(event) {
if (this === event.target) { // only fire this handler on the original element
alert('clicked');
}
});
Note that, if you have lots of elements in your page, this will be Really Very Slow, and it won't work for anything added dynamically.
What you really want to do is bind the event handler for the capture phase of the event. However, that isn't supported in IE as far as I know, so that might not be all that useful.
http://www.quirksmode.org/js/events_order.html
Related questions:
jQuery equivalent of JavaScript's addEventListener method
Emulate W3C event capturing model in IE
I know this is an old question, but to add to #lonesomeday's answer, you can do the same in vanilla JavaScript with:
document.querySelectorAll('*')
.forEach(element => element.addEventListener('click', e => {
console.log('clicked: ', e.target)
}))
This will add the listener to each element instead of to the body, and from experience this will let you execute the click event even if the page is navigating away or if there's already an onclick with stopPropagation in it.
I think this is what you need:
$("body").trigger("click");
This will allow you to trigger the body click event from anywhere.
You could use jQuery to add an event listener on the document DOM.
$(document).on("click", function () {
console.log('clicked');
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
document.body.addEventListener("keyup", function(event) {
if (event.keyCode === 13) {
event.preventDefault();
console.log('clicked ;)');
}
});
DEMO
https://jsfiddle.net/muratkezli/51rnc9ug/6/
My fix in Feb 2023:
To trigger a function anywhere on the page/document:
JS code:
document.onmouseup = closeMenus
'closeMenus' would be a function that turns each menu's display value to none.
Any 'mouseup' event anywhere on the document, calls the function.
I have a some elements, roughly like this:
<div>
<a>
<div>
When a user clicks anywhere on the div, I want the a element to be clicked - for usability purposes.
Simple right? So I wrote this:
$('div.class').click(function(){
$('a.class', this).click();
console.log('clicked');
});
Trouble is, this clicks the a element alright, but the event propagates to the div, which clicks it, which clicks the a, which... well you can see where it's going.
I cooked up a sample on JSfiddle here
but it doesn't show the console log. So if you click, Firebug doesn't show anything. but my local site sets Firebug crazy with logs (clicked) so much that in the end script gets killed saying too much recursion on this page
How do I stop this recursion?
Yes I know, I know that I can use window.location for this purpose, but clicking the link does some extra work and also uses window history for browsers, so I really want to click that vicious a without making it click its Dad. Or Mom. Or whatever that div is.
PLEASE READ
Since everyone is suggesting the same thing over and over again, and it's not working, please take a look this JSfiddle. Try it and see if it works before you answer. When you click on a div, Google should load up. That's what I'm looking for.
If this is is your markup:
<div>
<a></a>
</div>
...all you will need to do is in your css do something like:
div a { display: block};
The anchor element will then stretch and occupy all the available space in the parent div. However, if some other elements exist within that div, you could do:
$('a.class').click(function(event){
event.stopPropagation();
alert('you clicked on me');
});
$('div.class').click( function () {
$(this).children('a.class').trigger('click');
});
Use the event.stopPropagation() method.
How about this?
$('selector').attr('onclick')()
Instead of using the click event on the child node, just set the browser location to the href value
$('div.class').click(function(){
location = $(this).find('a').attr('href');
});
$('div.class').click(function(event){
event.stopPropagation();
$('a.class', this).click();
console.log('clicked');
});
You need to add the event argument and a stopPropagation method to the handler.
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)
I have a search input box that appears upon rollover of a button. Rather than having its own close button I would like to be able to click anywhere on the page to re-hide the Search.
If I attach a click handler to the document this works fine but the problem being is that the search itself is part oofthe document. So if you click the input (which of course you need to do in order to type in a search) the search disappers.
I had hoped I'd be able to write a function soemthing like this...
$(document).not("#search").click(function(){
$('#search_holder').fadeOut('fast');
});
i.e apply a click handler to the entire document APART from the search. Unfortunately that doesn't work.
so whats the answer?
thanking You in advance
Cancel the click event from propagating when it originates from the button you care about:
$("#search").click(function(event){
event.stopPropagation();
});
You can do it by stopping the click event from bubbling, like this:
$(document).click(function() {
$('#search_holder').fadeOut('fast');
});
$("#search").click(function(e) {
e.stopPropagation();
});
event.stopPropagation() prevents the bubble from going any higher, all the way to document triggering the .fadeOut(), everywhere else (by default) will bubble to document, causing the fade to occur.
Try this Its working perfect for me.
$(document).mouseup(function (e)
{
var searchcontainer = $("#search_container");
if (!searchcontainer.is(e.target) // if the target of the click isn't the container...
&& searchcontainer.has(e.target).length === 0) // ... nor a descendant of the container
{
searchcontainer.hide();
}
});