views:

259

answers:

2

I'm trying to create a drop-down menu acting as the sub-menu of a main menu. The sub-menu is simply a div element containing items/links. When a main menu item is clicked, the sub-menu drops down and stays there. That's all easy, but I want the sub-menu to slide back up if the cursor leaves the sub-menu. In other words, a simple 'mouseout' event. It seems, however, that when the cursor enters one of the items inside the sub-menu, the 'mouseout' event is triggered. That's what you would except, if you think about it, as the cursor does leave the sub-menu element even though it doesn't leave its boundries. However, this does pose a problem, because I only want the event to fire if the cursor is moved outside the boundries of the sub-menu element.

What it really boils down to, is having one div inside another div, like this:

----------------------------
|          DIV-1           |
|                          |
|      -------------       |
|      |           |       |
|      |   DIV-2   |       |   AREA OUTSIDE DIV-1
|      |           |       |
|      |           |       |
|      |           |       |
|      -------------       |
|                          |
|                          |
----------------------------

Now, 2 things may cause DIV-1 to fire a 'mouseout' event:

  1. The cursor moves from within the boundries of DIV-1 to the area outside those boundries
  2. The cursor moves from within the boundries of DIV-1 to the area of DIV-2

My goal is to be able to distinguish these two occurences from one another, yet I haven't been able to figure out how.

Does anyone have a good solution for this problem? It seems a common enough feature, so someone must have solved it.

A: 

Since mouseover events from child elements bubble/propagate to the parents, I would use a timer that is cancelled in the onmouseover event of the sub menu's div, if I was going the non-library route:

var hideMenuTimer;
subMenuDiv.onmouseover = function () {
    window.clearTimeout(hideMenuTimer);
}
subMenuDiv.onmouseout = function (evt) {
    evt = evt || window.event;
    if ((evt.target || evt.srcElement).id == "subMenuDiv")
        hideMenuTimer = window.setTimeout(function () {
            subMenuDiv.style.display = "none";
        }, 300);
}

A very simple example but it should work, providing all sub elements of the sub menu div correctly bubble the onmouseover event to the subMenuDiv element then the timer is cancelled before it polls. Also, I've put a 300ms timeout because I think it's best to have this sort of thing so that if you accidentally mouseout you have a short window to get the mouse back in before it hides. If you want it to hide instantly, setting it to 0ms should work fine too.

Andy E
Brilliant! I'm still working on incorporating this into my code (using Prototype), but I can see where you're going with this. It's excelent because it also provides that nice to have delay before the menu disappears. For once event bubbling is going to work for me, and not against me :) Thank you so much for your help, Andy.
Johan Fredrik Varen
No worries, glad it helped :)
Andy E
I love it when people downvote without leaving a comment...
Andy E
yeah, weak sauce on the down vote, have a +1 for a reasonable solution.
Gabriel
@Gabriel: I think the downvote was due to browser incompatibilities. I wrote this answer 8 months ago when I didn't know much about coding outside IE. Still, if they'd left a comment explaining then I would have learned that sooner! Thanks for the upvote :-)
Andy E
+2  A: 

http://users.tpg.com.au/j_birch/plugins/superfish/ <-- save yourself the time, he already pulled out his hair for you (maybe not literally, though, don't know)

Dan Beam
Thanks, Dan. I'll be sure to look more into this one.
Johan Fredrik Varen
It's awesome dude. It succeeds where SO many others have failed.
Dan Beam