77

Say there are some elements floating around, and I'm trying to do some when I click ANYTHING(divs, body, whatever...) but the one specified (e.g. div#special).

I'm wondering if there's a better way to achieve this besides the following method I can think of...

$(document).bind('click', function(e) {
    get mouse position x, y
    get the element (div#special in this case) position x, y
    get the element width and height
    determine if the mouse is inside the element
    if(inside)
        do nothing
    else
        do something
});
8
  • 3
    possible duplicate of How to detect a click outside an element? Commented Jul 9, 2011 at 15:27
  • $(':not(div#special)').bind('click', function(e) { ... ? api.jquery.com/not-selector Commented Jul 9, 2011 at 15:28
  • @JimSchubert: Should work, I'd post it as an answer - it's simpler than the answers in the possible duplicate. However, it seems inefficient. Commented Jul 9, 2011 at 15:29
  • 1
    @Wesley: But it is not correct. The event handler will also be bound to elements inside div#special... Commented Jul 9, 2011 at 15:30
  • 4
    @Jim: :not(div#special, div#special *) would be correct. However I would avoid binding an event handler to every element. Making use of event delegation is much better in this case. Commented Jul 9, 2011 at 15:33

6 Answers 6

122

To handle the "do this except when this element is clicked" situation, the general approach is to add an event handler to the document which handles the "do this" case, then add another event handler to the "except this" element, which simply prevents the click event bubbling up to the document;

$('#special').on('click', function(e) {
    e.stopPropagation();
});

$(document).on('click', function (e) {
 // Do whatever you want; the event that'd fire if the "special" element has been clicked on has been cancelled.
});

See the event.stopPropagation() documentation. For those of you using versions earlier than jQuery 1.7 (as was the case when this question was asked), you won't be able to use on(); instead simple replace the 2 uses of on() with bind(); the signature in this case is the same.

Demo here; http://jsfiddle.net/HBbVC/

Sign up to request clarification or add additional context in comments.

5 Comments

No offense, but that is basically the same answer as given in the duplicate I linked to: stackoverflow.com/questions/152975/…
@Felix: I can see that now, but you hadn't posted the comment when I started writing the reply :(.
doesn't seem to work on mobile Safari, though I don't have problems anywhere else.
Your answer, in combination with Hugo Silva's answer here fixed my particular issue on Safari. Mobile won't always allow a "click" action on non-anchors so you have to define it, it seems (see his answer for a proper explanation): stackoverflow.com/questions/10722730/…
This doesn't work if event handlers targeting other elements are defined that prevent bubbling up. This will result in those elements to be considered false positives. Imho, a better/full-proof solution is to define a document handler (as done), but define it to fire in the capture phase, instead of bubbling phase. You'd have to actively filter in said handler which elements you want to skip/pass-through. You can omit the other handler.
50

You could also do

$(document).bind('click', function(e) {
  if(!$(e.target).is('#special')) {
    // do something
  }
});

or if div#special has child elements you could do

$(document).bind('click', function(e) {
  if($(e.target).closest('#special').length === 0) {
    // do something
  }
});

1 Comment

Works well when I wanted a simple solution. Thanks! - $(document).on('click', function(e) {}
5

I've done it like this in the past:

jQuery("body").bind("click", function(e)
{
    var obj = (e.target ? e.target : e.srcElement);
    if (obj.tagName != 'div' && obj.id != 'special')
    {
        // Perform your click action. 
        return false;
    }
});

This would only execute if you didn't click on div#special. Honestly there may be better ways to do it, but this has worked for me.

1 Comment

You don't need to test for e.target. It will always be there, jQuery normalizes the event object. And it will not work if div#special contains other elements.
1

you need to do different binds, there is no need to process all this clicks in one function

$('body').bind('click', function(e){
  bodyClickEvent();
});
$('div.floating').bind('click',function(e){
  elementClickEvent(this);
  e.stopPropagation(); //prevents bodyClickEvent
});
  $('div#special').bind('click', function(){
  e.stopPropagation(); //prevents bodyClickEvent
});

Comments

1

I wrote this today for an issue i was having as i don't like having click events bound to the document the whole time, so for my scenario this works, using callbacks from functions.

$('#button').click(function(){
        //when the notification icon is clicked open the menu
        $('#menu').slideToggle('slow', function(){
            //then bind the close event to html so it closes when you mouse off it.
            $('html').bind('click', function(e){
                $('#menu').slideToggle('slow', function(){
                    //once html has been clicked and the menu has closed, unbind the html click so nothing else has to lag up
                    $('html').unbind('click');
                });
            });
            $('#menu').bind('click', function(e){
                //as when we click inside the menu it bubbles up and closes the menu when it hits html we have to stop the propagation while its open
                e.stopPropagation();
                //once propagation has been successful! and not letting the menu open/close we can unbind this as we dont need it!
                $('#menu').unbind('click');
            });
        });
    });

Comments

0

Just check if your element is hovered ;)

IMHO a super simple solution that works

$(document).on('click', function (e) {
  
  if($('#special:hover').length > 0){
    // on our special element
  }else{
    // not on our special element
  }
  
});

Cheers

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.