onmouseleave event in IE

J

jaysonnward

Hello All:

I've recently been recreating some 'dropdown menus' for a website I
manage. I'm writing all my event handlers into my .js file. I've got
the coding to work in Firefox, but the onmouseleave / onmouseout event
I've attached to my hidden drop down (in this case an <ul>), is not
firing correctly. It seems that when the mouse enters the ul it fires
the mouseleave event. The problem is in the hideMenu2(e) function.
I'm copying and pasting my code and html. If someone can help me
debug, I'd really appreciate it. Also, if the code could be improved,
please let me know.
Thanks
J
------------------HTML BELOW------------------------------
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"
/>
<link rel="stylesheet" type="text/css" href="assets/default.css" />
<script type="text/javascript" language="javascript"
src="assets/dropDowns.js"></script>
<title>JavaScript Testing</title>
</head>
<body>
<div id="mainDiv">
<div id="navigation">
<h1 id="menu1Link">MENU1</h1>
<!-- hidden menus -->
<ul id="menu1" class="dropDownMenu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1 - This is a long link</a></li>
</ul>
<h1 id="menu2Link">MENU2</h1>
<!-- hidden menus -->
<ul id="menu2" class="dropDownMenu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
</ul>
<h1 id="menu3Link">MENU3</h1>
<!-- hidden menus -->
<ul id="menu3" class="dropDownMenu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
</ul>
<h1 id="menu4Link">MENU4</h1>
<!-- hidden menus -->
<ul id="menu4" class="dropDownMenu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
</ul>
<h1 id="menu5Link">MENU5</h1>
<!-- hidden menus -->
<ul id="menu5" class="dropDownMenu">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 1</a></li>
</ul>
</div>
<p style="clear:both;"></p>
<p id="writeInfo"
style="vertical-align:bottom;margin-top:300px;">Info</p>
<p id="otherInfo">Stuff</p>
</div>
</body>
</html>

-------------------JAVASCRIPT CODE BELOW----------------------------

window.onload = init; //runs the init function when the page is loaded.


function init() //function to be run on page load
{
for (var i=1; i<6; i++) //Creates a for loop that attaches an event
listener to each of the h1's (based on id) on the page that make the
drop down appear
{
var menuLink = document.getElementById('menu' + i + 'Link'); //Sets
variable menuLink to document.getElementById('menu1Link') for example.
var dropDownMenu = document.getElementById('menu' + i); //Sets
dropDownMenu to document.getElementById('menu1') for example

if(menuLink.addEventListener) // Condition for Netscape, Firefox
{
menuLink.addEventListener("mouseover", showMenu, false);
menuLink.addEventListener("mouseout", hideMenu, false);
dropDownMenu.addEventListener("mouseout", hideMenu2, false);
}
else if(menuLink.attachEvent) // Condition for IE
{
menuLink.attachEvent("onmouseover", showMenu);
menuLink.attachEvent("onmouseout", hideMenu);
dropDownMenu.attachEvent("onmouseout", hideMenu2);
}
else // Condition if the previous conditional statements aren't
supported. These are traditional scripts and should work on most older
browsers
{
menuLink.onmouseover = showMenu;
menuLink.onmouseout = hideMenu;
dropDownMenu.onmouseout = hideMenu2;
}
}
}

function showMenu(e) //e == event (e.g. "mouseover") For use with the
element that displays the drop down menu
{
if(!e) var e = window.event; //IE doesn't register an event with the
function showMenu, but it does create a window.event variable, so we
set e to window.event which holds the event registered

var trueTarget = (window.event) ? e.srcElement : e.target; //sets the
trueTarget variable to e.src.Element if IE because checks to see if
window.event is true and/or supported, else it sets it to e.target for
all real web browsers

switch(trueTarget.getAttribute('id')) //switch statement gets the id
attribute for the mouseover event element(in this case the h1) and
calls the show menu function for the appropriate drop down menu
{
case 'menu1Link':
dropDownMenuShow('menu1');
break;
case 'menu2Link':
dropDownMenuShow('menu2');
break;
case 'menu3Link':
dropDownMenuShow('menu3');
break;
case 'menu4Link':
dropDownMenuShow('menu4');
break;
case 'menu5Link':
dropDownMenuShow('menu5');
break;
}
}

function hideMenu(e) //e == event (e.g. "mouseout") For use with the
element that displays the hidden drop down
{
if(!e) var e = window.event; //IE doesn't register an event with the
function showMenu, but it does create a window.event variable, so we
set e to window.event which holds the event registered

var trueTarget = (window.event) ? e.srcElement : e.target; //sets the
trueTarget variable to e.src.Element if IE because checks to see if
window.event is true, else it sets it to e.target for other browsers


//Find the mouses position when it rolls off the drop down target
element. This is to establish where the mouse is going
var mouseX = 0; //set a variable to hold x value
var mouseY = 0; //set a variable to hold y value

if(e.pageX || e.pageY) //if pageX and pageY are supported (Firefox,
Netscape)
{
mouseX = e.pageX; //sets x
mouseY = e.pageY; //sets y
}
else if(e.clientX || e.clientY) //in IE clientX and clientY replace
pageX and pageY
{
mouseX = e.clientX + document.body.scrollLeft +
document.documentElement.scrollLeft; //sets value by adding x and then
adding relevant scrollLeft, older IE browsers use document.body, newer
IE browsers use document.documentElement
mouseY = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop; //see above comment
}

switch(trueTarget.getAttribute('id'))
{
case 'menu1Link':
dropDownMenuHide('menu1', mouseX, mouseY)
break;
case 'menu2Link':
dropDownMenuHide('menu2', mouseX, mouseY)
break;
case 'menu3Link':
dropDownMenuHide('menu3', mouseX, mouseY)
break;
case 'menu4Link':
dropDownMenuHide('menu4', mouseX, mouseY)
break;
case 'menu5Link':
dropDownMenuHide('menu5', mouseX, mouseY)
break;
}
}

function hideMenu2(e) //For use with the drop down menu only
{
if(!e) var e = window.event; //IE doesn't register an event with the
function showMenu, but it does create a window.event variable, so we
set e to window.event which holds the event registered

if(e.target) //For Firefox, etc.
{
var trueTarget = (window.event) ? e.srcElement : e.target; //sets
the trueTarget variable to e.srcElement if IE because checks to see if
window.event is true, else it sets it to e.target for other browsers

if(trueTarget.nodeName != 'UL' || trueTarget.tagName != 'UL') //if
the element that generated the mouseoff event is not the ul itself
(meaning the a links inside), then return nothing and leave the drop
down visible
{
return;
}

var relTarget = (e.relatedTarget) ? e.relatedTarget : e.toElement;
//sets relTarget to e.relatedTarget if IE, and e.toElement if other

var relTargetParentNode = relTarget.parentNode.nodeName; //IE SEES
BODY WHILE FOX SEES HTML

while (relTarget != trueTarget && relTarget.nodeName != 'BODY') //
if the mouse wasn't moving onto the UL(or div, etc.) or the BODY
{
relTarget = relTarget.parentNode; //Then the element the mouse
moved onto is set to the parent node of the element (in this case the
UL)
} //This is for when the mouse moves onto an adjcent LI or
A within the parentNode (in this case the UL)
//If the mouse moves onto the Body then switch statment
below is fired

if(relTarget == trueTarget) // Keeps the drop down visible if moving
from element to element within the parentNode (e.g. UL)
{
return;
}

switch(trueTarget.getAttribute('id')) //If the mouse moves off the
parentNode onto the BODY or other element, then this switch is fired
and the appropriate menu becomes invisible
{
case 'menu1':
window.setTimeout("document.getElementById('menu1').style.visibility
= 'hidden'", 200);
break;
case 'menu2':

window.setTimeout("document.getElementById('menu2').style.visibility =
'hidden'", 200);
break;
case 'menu3':

window.setTimeout("document.getElementById('menu3').style.visibility =
'hidden'", 200);
break;
case 'menu4':

window.setTimeout("document.getElementById('menu4').style.visibility =
'hidden'", 200);
break;
case 'menu5':

window.setTimeout("document.getElementById('menu5').style.visibility =
'hidden'", 200);
break;
}
}
else // For IE
{
var trueTarget = e.srcElement; //sets the trueTarget variable to
e.srcElement if IE because checks to see if window.event is true, else
it sets it to e.target for other browsers

if(trueTarget.nodeName != 'UL' || trueTarget.tagName != 'UL') //if
the element that generated the mouseoff event is not the ul itself
(meaning the a links inside), then return nothing and leave the drop
down visible
{
return;
}

var relTarget = e.toElement; //sets relTarget to e.relatedTarget if
IE, and e.toElement if other

var relTargetParentNode = relTarget.parentNode.nodeName;//IE SEES
BODY WHILE FOX SEES HTML

while (relTarget != trueTarget && relTarget.nodeName != 'BODY') //
if the mouse wasn't moving onto the UL(or div, etc.) or the BODY
{
relTarget = relTarget.parentNode; //Then the element the mouse
moved onto is set to the parent node of the element (in this case the
UL)
} //This is for when the mouse moves onto an adjcent LI or
A within the parentNode (in this case the UL)
//If the mouse moves onto the Body then switch statment
below is fired

if(relTarget == trueTarget) // Keeps the drop down visible if moving
from element to element within the parentNode (e.g. UL)
{
return;
}

switch(trueTarget.getAttribute('id')) //If the mouse moves off the
parentNode onto the BODY or other element, then this switch is fired
and the appropriate menu becomes invisible
{
case 'menu1':
window.setTimeout("document.getElementById('menu1').style.visibility
= 'hidden'", 200);
break;
case 'menu2':

window.setTimeout("document.getElementById('menu2').style.visibility =
'hidden'", 200);
break;
case 'menu3':

window.setTimeout("document.getElementById('menu3').style.visibility =
'hidden'", 200);
break;
case 'menu4':

window.setTimeout("document.getElementById('menu4').style.visibility =
'hidden'", 200);
break;
case 'menu5':

window.setTimeout("document.getElementById('menu5').style.visibility =
'hidden'", 200);
break;
}
}
}

function dropDownMenuShow(menuNumber)
{
var menuLink = menuNumber + "Link"; //Create variable that will get
attributes for the list item for each dropDownMenu

var parentLeft =
document.getElementById(menuLink).offsetParent.offsetLeft; //gets the
parent Elements offsetLeft x value. REQUIRED FOR IE
var menuLeft = document.getElementById(menuLink).offsetLeft +
parentLeft; //gets drop down target link (e.g. h1) offsetLeft and adds
it to the offsetParents left to get the true left position.
var menuBottom = document.getElementById(menuLink).offsetTop +
document.getElementById(menuLink).offsetHeight; //finds the top for the
drop down by adding the top of the element to the height, gives you the
x value for the bottom of the drop down target element

document.getElementById(menuNumber).style.top = menuBottom + "px";
//Sets the top of the drop down menu relative to the eventtarget
document.getElementById(menuNumber).style.left = menuLeft + "px";
//Sets the left position of the drop down relative to the event target

document.getElementById(menuNumber).style.visibility = "visible";
//Sets the drop down to visible
}

function dropDownMenuHide(menuNumber, xPosition, yPosition)
{
var x = xPosition;
var y = yPosition;
var menuLink = menuNumber + "Link"; //Create variable that will get
attributes for the list item for each dropDownMenu
var linkLeft = document.getElementById(menuLink).offsetLeft; //gets
drop down target link (e.g. h1) offsetLeft
var linkRight = linkLeft +
document.getElementById(menuLink).offsetWidth; //to get right x
position, add left x position to element's width
var linkTop = document.getElementById(menuLink).offsetTop; //gets
elements top y position
var linkBottom = document.getElementById(menuLink).offsetTop +
document.getElementById(menuLink).offsetHeight; //finds the top for the
drop down by adding the top of the element to the height, gives you the
x value for the bottom of the drop down target element

if(x <= linkLeft || x >= linkRight || y <= linkTop) //if the mouse
moves to the right, left or top of the firing element, the menu will
disappear
{
document.getElementById(menuNumber).style.visibility = 'hidden';
}
else //if the mouse moved below the firing element, the menu will stay
visible until the mouse rolls off the menu, which will fire the
hideMenu2 function
{
document.getElementById(menuNumber).style.visibility = 'visible';
}

}
 
J

jaysonWard

Thanks for the input David.

Yeah, that was a lot of code to put in. I'm at work and I didn't have a
place I could put the stuff as a link. Thanks for reviewing though.

So:

var trueTarget = e.target || e.srcElement; works just like

var trueTarget = (window.event) ? e.srcElement : e.target;

This is true because the browser will assign trueTarget to the event
property it understands (and you don't need to check to see if
window.event is understood, by IE obviously)?

I'll test out the switch statement changes and then post back.

Thanks again
J
 
R

RobG

David said:
(e-mail address removed) wrote: [...]
if(!e) var e = window.event;
This doesn't (or shouldn't) work. Instead use:

There's nothing technically wrong with it - if nothing is passed to the
e parameter, it will be undefined and hence evaluate to false.
However...
e = e || window.event;

Is preferred. An old version of Safari requires:

var e = e || window.event;

There is nothing harmful about using var with an already declared
variable, though it should only be used when it is first declared.

Why? Well, you're creating a new var e within the scope of that if
statement that won't necessarily be around outside that scope.

Javascript doesn't have block-level scope. A variable declared
anywhere in a function is available anywhere else in the function, but
not outside it. Importantly, functions can access variables declared
in "outer" functions but not "inner" functions:

function foo(){
function bar(){ ... }
}

bar has access to all the variables of foo, but foo has no access to
those in bar. Declaring a local variable in bar with the same name as
one in foo will mask the one in foo.

I'm not
100% sure if that's the case here, since you don't use braces, but in
general it's bad practice and you should avoid declaring variables
using the "var" keyword within "if" statements like this (because it'll
get you into trouble when you use it within braces).

It may get you into trouble in other languages, but not javascript. It
is generally considered good practice to declare variables outside
blocks using var. Some like to declare them all at the start of the
function, others immediately before the block they'll be used in.
Different strokes... :)
You already have
a variable identified by "e" - passed in as an argument. So you don't
need to declare it again using "var".

This will work, but more concisely:

var trueTarget = e.target || e.srcElement;

Ok, I think this might be where your problem comes in. IE sometimes
doesn't return an attribute "id" and you have to use trueTarget.id, but
then Mozilla (correctly) doesn't recognize the "id" property, so you
have to use both:

Are you sure? I've never encountered a case where Mozilla (or any
other browser) barfed on element.id.

The OP's use of switch is totally unnecessary, the element id could be
extracted using a regular expression and placed directly into the call
to dropDownMenuShow. Similarly, that function doesn't need a switch
statement either. The whole concept of hard-coding the IDs is bad
practice and makes the whole thing a maintenance nightmare.
I also noticed a lot of "copy-and-paste" code, such as code to make
elements visibly or invisible, repeated in multiple places. Why not
write a function that does this and call it? In fact a lot of your
code, while thorough, can be refactored for reuse and concision.
Anywhere you find yourself wanting to copy and paste some code, ask
yourself "Can I write a function that does this? What variables would
that have?" It's worth writing even one-line functions for
commonly-used statements. Imagine the work you'll have to do - and the
bugs you might introduce - if you have to change that statement in the
future!

Full agreement. The OP should look at pure CSS dropdowns, or at least
ones that use considerably less code.
 
J

jaysonWard

Rob:

Thanks for the input. I appreciate the critique of my coding.

When you say pure CSS drop downs, that's what I thought I was doing by
having the "dropdowns" as hidden <ul>s that you would use javascript on
the <h1>'s to show. Is there a way to do this with pure CSS that I
haven't encountered?

Once again, your help is appreciated.

Jayson
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,767
Messages
2,569,571
Members
45,045
Latest member
DRCM

Latest Threads

Top