Skip to the content...

Welcome to the David Walsh Blog. I'm a MooTools, Dojo, jQuery, CSS, and PHP Web Developer located in Madison, Wisconsin, United States. Please contact me if I can make your experience on my website better.

Track Clicks Outside Active Elements with MooTools or jQuery

23 Responses »

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!

Discussion

  1. March 18, 2010 @ 8:22 am

    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. March 18, 2010 @ 10:01 am

    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
    March 18, 2010 @ 10:08 am

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

  4. March 18, 2010 @ 10:23 am

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

  5. March 18, 2010 @ 10:42 am

    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. March 18, 2010 @ 10:56 am

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

  7. mlazz
    March 18, 2010 @ 11:19 am

    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. March 18, 2010 @ 12:26 pm

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

  9. jamie
    March 18, 2010 @ 12:56 pm

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

  10. March 18, 2010 @ 1:24 pm

    @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
    March 18, 2010 @ 1:27 pm

    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. March 18, 2010 @ 1:37 pm

    @jamie: Ummmm…my jQuery version is working…

  13. jamie
    March 18, 2010 @ 6:59 pm

    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
    March 18, 2010 @ 7:25 pm

    lol@ Dr. Death’s photo

    Could something be done with event delegation?

  15. March 18, 2010 @ 7:49 pm

    @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. March 18, 2010 @ 8:33 pm

    @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. March 18, 2010 @ 11:16 pm

    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. March 18, 2010 @ 11:26 pm

    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
    March 21, 2010 @ 6:37 am

    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
    March 24, 2010 @ 6:28 pm

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

  21. kirill
    April 14, 2010 @ 8:10 am

    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. April 26, 2010 @ 11:03 pm

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

  23. May 2, 2010 @ 5:29 pm

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

    @David Walsh:

Be Heard!

Share your thoughts with fellow developers of all skill levels! I want to hear from you!

Name*:
Email*:
Website:  
Wrap your code with <code> tags, f00!