TheDrunkenEpic

Hello, I'm Wilhelm, a PHP developer, and this is my little corner of the internet where I can discuss things that interest me and, hopefully, you too. :-)


Currently Reading:

26Sep 2008

jQuery: Is a 'mousedown' event within a specified boundary?

While working today I came across a problem I haven't encountered before. At my current place of employment, we're implementing a new navigation system for thier home-grown CRM. It's modeled much like the Microsoft Office 2007's ribbon navigation system where you have menu bars that are relevant to the current user's status, position, etc. Some of these bars have buttons that you click and have contextual menus or forms appear.

The current goal for this drastic navigational change is to allow users to perform repetitive tasks as fast as possible. In one instance, a user might enter a customer call into the system 8 or 10 times a day. The older method of doing this particular task was cumbersome and time-consuming. While trying to make this process as efficient as possible we came up with some creative ways to streamline it.

Without dragging this background story out too much, one of the issues I was confronted with was how to exit a contextual form. We're trying to come up with an intuitive and responsive solution, but, due to the nature of the system, we're somewhat limited to what we can. Unlike Microsoft, we don't have a massive ready-made and time-tested library for creating UI elements, so some things, even the simplist kinds of functionality you'd come to expect from a desktop application, need to be engineered from scratch.

Say, for instance, you have a contextual menu open in Windows by right-clicking on an object. The menu stays until you either click on an option or click outside of it's boundaries. The same functionality was needed for these contextual forms. If a user decides, "Meh, wtf mate? I reckon I don't need to finish this bloody form. Sod off and bullocks to you, good sir... In the name of the Queen.", we wanted them to be able to click anywhere on the page that didn't reside within the confines of the form itself and have it close automagically.

So, with a bit of creativity, and almost no research into determining whether or not this was already done elsewhere, I came up with the following solution!

To do this, we need only check either of the following conditions

  1. Did the mouseclick occur inside the boundary
  2. Did the mouseclick occur outside the boundary

The boundary can be any element within the document. In our example, we'll just use a small 50 by 50 pixel div element with the id of boundary:

<div id="boundary" style="height: 50px; width: 50px; background: #000 none; cursor: pointer;"></div>

Now that we've defined a clickable area to test with, we need to start tracking the actual clicks. We do this by binding a 'mousedown' event the entire document:

$(document).bind('mousedown', function(click) {
    return checkMouseBoundary(click, '#boundary');
});

Now we can track any click within the current page. Function checkMouseBoundary(click) will be called every time a mouse has been clicked within the document. The mousedown event will pass the metadata concerning the actual click to this callback function:

function checkMouseBoundary(click, boundary)
{
    var element = $(click.target);
    var boundary = $(boundary);

    while(true)
    {
        if(element == boundary)
        {
            return alert('I am inside the boundary ...');
        }
        else if(element == document)
        {
            return alert('I am outside the boundary ...');
        }
        else
        {
            element = $(element).parent();
        }
    }

    return true;
};

This is the brains behind the magic. This way seemed to make the most sense to me, so let's break it down step-by-step.

First, we need to identify the element where the click occurred. Since the mousedown event binding returns the information about the actual click, we can identify the target element by doing the following:

var element = $(click.target);

Next, we need to identify the boundary we set. In test case, we're using div#boundary:

var boundary = $('#boundary');

Now, if var element is equal to var boundary, the click originated within div#boundary and if they are not equal, it must have happened somewhere else, right? Well, yes and no.

What if div#boundary has child elements? If you click the child of the boundary, technically, you're still within said boundary. We have no way of knowing this unless we traverse the document, traveling up the element tree from the click target until we either first hit the element identified as div#boundary or travel straight up to the very top of document. For example, in our test case, if you click on anything between the body and div#boundary element, we would end up going straight to the top of the document. If we click on anything within div#boundary, no matter how many levels deep, and we travel up the element tree until we hit the actual boundary.

So, what we need to do is create a loop that iterates the document with every click starting from the target element:

while(true)
{
    if(element == boundary)
    {
        return alert('I am inside the boundary ...');
    }
    else if(element == document)
    {
        return alert('I am outside the boundary ...');
    }
    else
    {
        element = $(element).parent();
    }
}

The above loop does not stop until one of two conditions are met. If neither are met, find the current click target element's parent and keep on going. Eventually, we should meet either criterion and stop the loop.

Also, you don't have to use the id of #boundary for this to work. You can even test against any element that has a specific class name attached to it. So, if you have 5 different elements that use the class .omgwtfbbq, you can do the following:

$(document).bind('mousedown', function(click) {
    return checkMouseBoundary(click, '.omgwtfbbq');
});

And it should track every element with that class assigned to it.

When you're done, always be sure to release the bound event like so:

$(document).unbind('mousedown');

This will release all mousedown events bound to document. No point in keeping them around if we're not using them, right?

There you go! Now, you can check if someone clicks outside of an element and act accordingly. This example should be cross-browser compatible, but if you have any issues, please report them within this article. I hope you found this little tutorial worth your time.


Your Commentary:

Nice man, very cool concept. I can't imagine an application interface I've built recently where this wouldn't be useful.

More stuff like this, bro! Seriously. :P

Lorren Biffin spoke the truth on September 29th at 6:25 pm #

I'll be sure to keep them coming. I'm running into plenty of instances with this new navigation system where neat stuff like this can come in handy. :)

Wilhelm Murdoch spoke the truth on September 29th at 6:26 pm #

Very cool, keep this stuff coming!

Sean Cannon spoke the truth on September 30th at 12:08 pm #

Hey, Sean! I plan on posting more things as I encounter them. :)

Wilhelm Murdoch spoke the truth on September 30th at 7:21 pm #

Hi Wilhelm! This is exactly what i need, but do you have an online example of this? It's not working for me. Firefox says that the script is not answering anymore.

Stefan spoke the truth on November 25th at 6:09 am #

TrackBack: http://www.thedrunkenepic.com/home/articles/trackback/115/

Leave a Comment: