Track Clicks Outside Active Elements with MooTools or jQuery

By  on  

Buried in the MooTools and jQuery code from my Twitter Dropdowns posts was a small but useful snippet of code: hide an "active" element when the user clicks outside of the elements.

Twitter Dropdown Menu

Take a look at the image above. When you click the "Menu 1" button, the dropdown menu displays. Both the buttons and dropdown are wrapped in a DIV. When the menu gets activated, that wrapping DIV (or "parent") is stored in a variable. For each dropdown, we add an event to the body to check where the user clicks. The following snippet explains what happens next.

The MooTools JavaScript

/*
- showingParent is the wrapper div.
- hideMenu hides the showingParent's dropdown, etc.
*/

/* hide when clicked outside */
$(document.body).addEvent('click',function(e) {
	if(showingParent && !e.target || !$(e.target).getParents().contains(showingParent)) { 
		hideMenu(); //hide the menu! clicked outside!
	}
});

The Event object (e, in this case), specifically Event.target, tells us which element was clicked. Once we know which element is clicked, we can step up the DOM tree, looking for the "parent" element we had saved. If we find the saved parent element, we know the click occurred inside the designated area and we do *not* close the active element. If we don't find the saved parent, the user clicked outside the active element and we should close the menu.

The jQuery JavaScript

$(document.body).bind('click',function(e) {
	if(showingParent) {
		var parentElement = showingParent[0];
		if(!$.contains(parentElement,e.target) || parentElement != e.target) {
			hideMenu();
		}
	}
});

The same principle applies; jQuery's syntax is simply different.

If you still have questions about the context of its use, check out the MooTools or jQuery Twitter Dropdown articles to see this technique in practice. This ability is extremely effective and valuable when trying to create usable dropdowns. Have fun with this!

Recent Features

Incredible Demos

Discussion

  1. Hey David, nice tip !

    Just one thing though, often, when we build menu like these, we add another action for hiding the menu if we doesn’t click elsewhere. A kind of periodical() set to 1000ms that checks if the cursor is in the dropdown area or not.

    Do you have a clean and nice way to di it too ? Because the part that bugs me is when you have to check the cursor position… checking if it is in the dropdown area by getting the dropdown values top, left, width, height is a little…. weird I think, there’s maybe a better way.

    Thanks a lot, nice blog!

  2. Hi Keven,
    Instead of checking periodically, you could just do a 1000ms timeout on the function that hides the menu during mouseleave/mouseout event on your menu.

  3. Arian

    Also look at http://cpojer.net/blog/Custom_outerClick_event for a custom outerclick event

  4. @Arian: That didn’t work in this case.

  5. Hi David,

    very good article indeed. Actually I wrote about a month ago a clickoutside jQuery plugin. You may be interested. http://www.stoimen.com/blog/2010/02/17/clickoutside-jquery-plugin/

  6. See also Ben Alman’s recent Outside Events jQuery plugin:
    http://benalman.com/projects/jquery-outside-events-plugin/

  7. MLaZz

    Might be usefull for my MultiSelect plugin on Forge.. I have something like this, but this snippet is more effective.

    Wait for an update.. ;)

  8. maybe add native event “outerclick” to core with this function?

  9. jamie

    is there no demo to this? I can find lots of uses for something like this.

  10. @Dr.Death: Still trying to think of the best way to do that.

    @jamie: Check out the articles cited in the last paragraph of the post.

  11. jamie

    I did check out the articles. I didn’t like them. They are about clicking outside. I was more intersted in seeing the markup for the jquery to get the menu working. I guess I could figure it out myself. Just wanted to see how you would do it.

  12. @jamie: Ummmm…my jQuery version is working…

  13. jamie

    that’s why I was asking about a demo. I don’t see any working version you are talking about. I have looked all over for the jquery demo. You usually post one, but I can’t find it.

  14. EmEhRKay

    lol@ Dr. Death’s photo

    Could something be done with event delegation?

  15. @Sean O / @David Walsh: This approach (event delegation) is exactly the approach I took with my recently-released jQuery outside events plugin, except I used jQuery’s own special events API to actually create a whole new, custom event for clickoutside that can be bound to and triggered using the standard jQuery events methods.

    In addition, I’ve created an “outside” event for every native jQuery event that madee sense, as well as included a method to allow users to create their own. Feel free to check out the examples on the project page, on my site!

    http://benalman.com/projects/jquery-outside-events-plugin/

  16. @jamie: http://davidwalsh.name/dw-content/twitter-dropdown-jquery.php

    @“Cowboy” Ben Alman: I’ll be creating the MooTools custom event this weekend. Nice work on your jQuery version!

  17. Very nice bro, I’ve been using while loops in this format to do much the same thing for a while now for ordering visual sitemaps.

    // Bubble up to get the ROW that is hovered over
    over = event.target;
    		
    while (over && over.nodeName != this.options.childTag) {
    over = over.parentNode;
    }
    

    I will test out my class with your code VS the while loops and see which one is faster.

  18. Argh your pesky head made me click the submit button before i finished writing that…

    The following is how I’d typically determine if an event target lived inside of the parent.

    var parent = parentNode
    check = e.target;
    while (check && check != parent) {
        check = check.parentNode;
    }
    

    It either winds up being null or the parent your looking for.

    So those are the two states your looking for.

    Never even knew MooTools had a .getParents() method.

    Will be interesting to see which one performs better.

  19. hamburger

    i did it like this:

    // if click outside the box .pop_dialog_table close it
    $(document.body).addEvent('click',function(e) {
        var x = $($pick(e.target,e.srcElement));
        if(log.getStyle('opacity').toInt() == 1 && !x.getParent('.pop_dialog_table'))
          {
            log.fade('out');
          }
    });
  20. venu

    @jamie: yes iam also want to see the demo for this

  21. Kirill

    Thanks! That really helped me. Thought I had to attach the click event to the window, because the body of the document doesn’t always extends to the entire window.

  22. @Kirill: It would be the HTML tag, if anything…

  23. Just wondering if you did create the custom event for MT…

    @David Walsh:

  24. Still don’t add “outerclick” event?

  25. This is just brilliant ! thank you for sharing, 2 minutes to find it, 2 minutes to put in in my menu script and it works !
    CEd

  26. Shouldn’t it be:

    !$.contains(parentElement,e.target) && parentElement != e.target

    ?

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!