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)
Related
I use jQuery 1.6.2. I want to call a click event just for once when that element appears at page. This is my selector:
span[resizer='north'][title=''] :first-child"
This can work for triggering click event:
$("span[resizer='north'][title=''] :first-child").click();
How can I make it work only once or after that element appears on the page?
Answer as per how I understand your last comment: I mean I will call click event only once. Let's assume that there is a button. I will call click event of that button once. I mean I will programmatically click that button.
$('button').click(function() {
$('span[resizer='north'][title='']:first-child').click();
$(this).unbind('click');
});
You may use DOMNodeInserted event to detect when new element is inserted dynamically.
var alreadyclicked = false; // variable to check that it will be clicked just once
$(document).bind('DOMNodeInserted', function(e) {
if (!alreadyclicked){
$("span[resizer='north'][title=''] :first-child").click();
alreadyclicked = true;
}
});
Update
The DOMNodeInserted event is deprecated along with other mutation events. Some browsers support it, but looks like this won't last long.
You can use mutation observers instead to keep track of changes in DOM
var alreadyclicked = false;
var observer = new MutationObserver(function(mutations, observer){
//if new nodes were added
if( mutations[0].addedNodes.length && !alreadyclicked )
$("span[resizer='north'][title=''] :first-child").click();
alreadyclicked = true;
});
observer.observe( document, { childList:true, subtree:true });
Use a custom event, with a delegated handler attached using .one().
An event has 2 components, triggering and handling. Don't focus on how many times it's triggered, focus on how many times it's handled.
$("document").one("myclick", "span[resizer='north'][title=''] :first-child", function(e) {
do something...;
$(this).click(); //if you actually need the "click"
});
$("span[resizer='north'][title=''] :first-child").trigger("myclick");
This example will mean it's "handled" one time. The other answers on here can be used to ensure it's triggered when the element is added if it's after document ready.
You could also use a pub/sub which would make it even easier. Then you just have to trigger your custom event from the ajax or whatever code that would add the element and not worry about specifically targeting the element. Last line of code in the subscription would be to unsubscribe.
I am cloning a div tag when user click on available div. After cloning it I am inserting it after clicked div tag. This new div tag also has cross link to delete it. However when I click on cross link it does delete that particular div. It means it is working. Now I also have one function named foo. This method is called upon adding and deleting div tag. But when I click on delete link, this foo function is called twice. I know its reason because I am attaching click event to both parent of delete link and to delete link itself therefore, it is called twice. But How can I make it call once upon both adding and deleting div element?
Here my JSFIDDLE
Html:
<div class="container">
<div class="bx"></div>
</div>
Here is js
$('body').on('click', '.bx', function (e) {
var clone = $(this).clone();
clone.append('<a class="bx-del" href="#">X</a>');
$(this).after(clone);
foo();
});
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
$(this).parent().remove();
foo();
});
function foo() {
console.log('foo');
}
My original answer was :
Assuming .bx-delis inside .bx(better to add html as well in the question) then adding e.stopPropagation() to the .bx-delcallback should work.
E.g.
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
e.stopPropagation();
$(this).parent().remove();
foo();
});
Documention here
It has been pointed out that both events are actually attached to the body and run as delegated events. This solution seems to work but the question is whether or not it is reliable. The question here is in what order are delegated events run? Are they always run "depth first" or based on the order they were added or what? Running them depth first would be the most logical in my view, but performance considerations will play a large role here too.
I have been unable to find any hard documentation on this. The jQuery .on() documentation says
jQuery bubbles the event from the event target up to the element where
the handler is attached (i.e., innermost to outermost element) and
runs the handler for any elements along that path matching the
selector.
which could be interpreted as saying that, but could also be an explation of the concept of delegation.
Thus a safer solution to the the original problem, in my view, would be to combine the two lick events into one:
$('body').on('click', '.bx', function (e) {
var t = $(e.target);
if( t.hasClass('bx-del') || t.closest('.bx-del').length > 0){
e.preventDefault();
t.closest('.bx').remove();
foo();
} else {
var clone = $(this).clone();
if( $('.bx-del', clone).length == 0 ){
clone.append('<a class="bx-del" href="#">X</a>');
}
$(this).after(clone);
foo();
}
});
(I have also fixed so that cloning a div with a delete button doesn't add a second delete button).
This is what event.stopPropagation() is for (docs). It prevents the event from bubbling up into the DOM tree. If you add e.stopPropagation() to your delete link click handler, it will not bubble up to the div that contains it and trigger the click handler there too:
As pointed out in the comments, this is not correct, even though it appears to work correctly. Both event handlers are attached to body, and only via a 'trick' run on specific elements. The event is already at the body element, and will therefore not bubble any further. Furthermore, event.stopPropagation() should not stop other handlers from execution, event though they seem to do that right now anyway. The correct function to use here is event.stopImmediatePropagation() (docs at jquery).
$('body').on('click', '.bx-del', function (e) {
e.preventDefault();
e.stopImmediatePropagation();
$(this).parent().remove();
foo();
});
Because you cannot stopPropagation to the <body> tag when the event has already propagated to the <body> tag and both your events are on the <body> tag, I would suggest just attaching the delete event handler right to the delete X object and not use propagation for that event handler. You can then stopPropagation of that click so it won't be seen by the parent at all. In your case this is fairly easy.
Working demo: http://jsfiddle.net/jfriend00/7CDNG/
$('body').on('click', '.bx', function (e) {
var clone = $(this).clone();
clone.append('<a class="bx-del" href="#">X</a>');
$(this).after(clone);
clone.find('.bx-del').on('click', function(e) {
$(this).parent().remove();
foo();
// stop propagation and prevent default
return false;
})
foo();
});
function foo() {
console.log('foo');
}
Alternatively, you could put the .bx handler on the document object and the .bx-del handler on the body object and then you could stopPropagation up to the document object from the .bx-del handler. But, it seems in this case, it is much cleaner to just attach the delete event directly to the delete object and not use delegated event handling to avoid any possible misinterpretation of the click event.
Apparently the other answers are getting away with using .stopPropagation() on one body event handler to stop the other one from firing. Here's a quote from the jQuery documentation for .stopPropagation(): "I think you're getting lucky that's all. This is taken directly from the jQuery documentation for stopPropagation(): "Note that this will not prevent other handlers on the same element from running." Since both of these event handlers are on the body object, it does not seem like a wise and safe move to use .stopPropagation() from one to stop the other from running. It's my opinion that that technique is not safe and is not the conceptually correct way of solving this problem.
I'm comparing click events in jQuery to see if they fired from the same click. Right now I'm using the events' timeStamp to test to see if the events came from the same click, and while this will almost certainly work, I'm wondering if there's a better way.
For example, if body and header both have a click event, and I click on the header, it will bubble up to the body. What is the best way to test if both events that fire come from the same click?
In plain JavaScript, the same event object will reach both event listeners, so you can compare them. jQuery on the other hand will wrap the original event object into a new normalized object for each listener, so you have to check their .originalEvent property instead.
In any case, I never needed to check for that. Usually, you just want to know if the clicked element is the same as the one you bound the click to, or not:
document.body.addEventListener('click', function(e) {
if(e.target === this) {
// the click happened on the body itself
} else {
// the click bubbled up
}
}, false);
jQuery provides a nice mechanism for event delegation, so you don't even need the if:
$('body').on('click', '.someSelector', function(e) {
// This will only fire if an element with class 'someSelector'
// was clicked
});
I'm trying to make an extension with 'pick' functionality: letting the user click on any element on the page and see the xpath for it.
However, to make it usable I have to prevent the element from reacting on clicking: I don't want clicking on hyperlinks to forward me to another page, buttons to submit forms, etc., etc.
How would I do that?
UPD: I need it only for Chrome
The cleanest way in my opinion would be to bind one event handler to body in the capturing phase and prevent the event from propagating/the default behavior.
You can get a reference to the clicked element with event.target:
document.body.addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
console.log(event.target);
}, true);
The advantage is that this event handler will be the very first that is triggered, no matter which other event handlers are bound to elements in the page (unless of course the page binds a similar event handler to body, but usually event handlers are bound in the bubbling phase).
For more information see quirksmode.org - Event order and MDN - addEventListener.
DEMO
For a CSS only solution:
* {
pointer-events: none;
}
Try this
$('body *').on('click',function(e){
return false;
});
In your click event handler make sure you either call preventDefault() in the event object, or return false from your function. For example:
function myClickHandler(e) {
e.preventDefault() // e is the event object
// Do your click stuff here
.
.
.
return false; // not required if you've called e.preventDefault()
}
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.