Welcome to WebmasterWorld Guest from 107.23.176.162

Forum Moderators: open

Message Too Old, No Replies

CSS and Javascript select menu

Emulating an HTML select menu with Javascript

     
12:51 pm on May 14, 2005 (gmt 0)

Junior Member

10+ Year Member

joined:May 6, 2005
posts:43
votes: 0


Hi all, I had to create a bespoke drop-down select menu because the options for applying CSS to standard HTML select boxes are too limited. Below is the code:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>SELECT BOX</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<style type="text/css">
<!--
body,td,th {
font-family: verdana;
font-size: small;
color: #009999;
}
body {
background-color: #FFFFFF;
margin-left: 2em;
margin-top: 2em;
}
-->

br{
line-height:0;}

#innermenu a{
text-decoration:none;
}

#innermenu a:hover{
background-color:#00FFFF;

}

div{
padding:0;
margin:0;
}

#search{
width:150px;
height:auto;
border:1px solid white;
}

a.drop{
display:block;
font-size:0.9em;
font-weight:normal;
color:#666;
text-decoration:none;
width:146px;
height:12px;
padding:1px;
background-image:url(arrow_out.gif);
background-repeat:no-repeat;
background-position:right;
text-align:left;
cursor:hand;
border:1px solid #006699;
}

a.drop:hover{
font-size:0.9em;
font-weight:normal;
color:#666;
text-decoration:none;
display:block;
width:146px;
height:12px;
padding:1px;
background-image:url(arrow_over.gif);
background-repeat:no-repeat;
background-position:right;
text-align:left;
cursor:hand;
border:1px solid #006699;
}

#dropdown{
font-size:0.9em;
z-index:10;
width:148px;
height:auto;
background-color:#FFFFFF;
border:1px solid #006699;
border-top:0;
visibility:hidden;
}
#dropdown ul{
padding:0;
margin:0;
list-style-type:none;}

#dropdown li{
padding:0;
margin:0;}

#dropdown a{
display:block;
background-color:#fff;
padding:2px 4px;
margin:0;
text-decoration:none;
color:#666;
}

#dropdown a:hover{
display:block;
background-color:#0099FF;
padding:2px 4px;
margin:0;
text-decoration:none;
color:#fff;
}

</style>

<script type="text/javascript">
function showhide(whichLayer)
{
if (document.getElementById)
{
// this is the way the standards work
var style2 = document.getElementById(whichLayer).style;
style2.visibility = style2.visibility? "":"visible";
}
}

</script>

</head>

<body>

<div id="search">
<a href="javascript:showhide('dropdown');" on class="drop">Show all links</a>

<div id="dropdown" onmouseout="showhide('dropdown');">
<a href="#"> Link 1</a><br />
<a href="#">Link 2</a><br />
<a href="#">Link 3</a><br />
<a href="#">Link 4</a><br />
<a href="#">Link 5</a><br />
<a href="#">Link 6</a><br />
<a href="#">Link 7</a><br />
<a href="#">Link 8</a><br />
</div>
</div>
</body>
</html>

The functionality works fine until the mouseout event is triggered by the dropdown box. Somehow the browser considers the mouse to be out of the dropdown div as soon as you hover over any of the links contained within that div. This obviously what causes that annoying blinking effect and I was wondering if there was anyway to stop this from happening.

Would anyone here know how to stop the browser from considering child elements of the dropdown divs to be outside of the div?

Thanks a lot in advance!

2:44 pm on May 14, 2005 (gmt 0)

Senior Member

WebmasterWorld Senior Member 10+ Year Member

joined:Apr 15, 2004
posts:2047
votes: 0


"..the browser considering child elements of the dropdown divs to be outside of the div?"

In fact, that's not what happening. Events like clicks and mouseouts bubble. There is a cacophony of mouseouts bubbling up from the child elements. If there is any gap between the child elements, then there are also mouseouts as the mouse moves to each child.

There are ways around this. These usually involve querying the event trapped at the event handler. It always involves some kind of cross-browser fiddles.

Personally, I think the widget would be more use-friendly if the dropdown didn't hide until the mouse moves off the top link. I mention this because I have a feeling it would also be a little easier to code for.

4:35 pm on May 14, 2005 (gmt 0)

Senior Member

WebmasterWorld Senior Member 10+ Year Member

joined:Apr 15, 2004
posts:2047
votes: 0


Here's a solution. The main point is that the on´mouseout handler has been moved to the containing DIV element. This has been given 5px padding. This is to make sure that the mouse cannot leave the widget without causing a mouseout on the container.

I have changed the CSS property to 'display'.

If the function call sends an event argument, showMenu function tests the event for the element that caused it. If it is not the container then the function returns.

#dropdown{
....
display:none;
}

....

<script type="text/javascript">
getEventSrc = window.Event
? function(e){var targ=e.target;return targ.nodeType==1?targ:targ.parentNode}
: function() {return event.srcElement}

function showMenu(container,Bshow, e)
{
if (
!document.getElementById ¦¦
(e && getEventSrc(e)!=container)
) return;
container.getElementsByTagName('div')[0].style.display
= Bshow? 'block':'';
}

</script>

</head>

<body>

<div id="search" onmouseout="showMenu(this,0,event);" style="padding:5px;">
<a href="#" onclick="showMenu(this.parentNode,1);" class="drop">Show all links</a>
<div id="dropdown" >
<a href="#"> Link 1</a><br />
<a href="#">Link 2</a><br />
<a href="#">Link 3</a><br />
<a href="#">Link 4</a><br />
<a href="#">Link 5</a><br />
<a href="#">Link 6</a><br />
<a href="#">Link 7</a><br />
<a href="#">Link 8</a><br />
</div>
</div>

[edited by: jatar_k at 3:47 pm (utc) on Oct. 5, 2005]

9:44 am on May 16, 2005 (gmt 0)

Junior Member

10+ Year Member

joined:May 6, 2005
posts:43
votes: 0


Thanks so much for the reply guys and Bernard Marx, thanks a lot for the code, I certainly learned a lot from it. And I can definitely see your logic of applying the mouseout event to the search div instead, but would I have problems if there were more than 1 combobox menus on the page, each contained within their own #Search like layer? Also, pardon my ignorance but what does the following line of code does exactly:

 getEventSrc = window.Event? function(e){var targ=e.target;return targ.nodeType==1?targ:targ.parentNode}
: function() {return event.srcElement}

I can see that you are capturing the event + finding the target but as a result of my limited of knowledge of of JavaScript & its syntax I can’t see how it would fit in the equation above. And I would like to understand it better as I’d probably be using something along these lines quite a lot in the future.

Regards,

BB

1:18 pm on May 16, 2005 (gmt 0)

Senior Member

WebmasterWorld Senior Member 10+ Year Member

joined:Apr 15, 2004
posts:2047
votes: 0


"but would I have problems if there were more than 1 combobox menus on the page, each contained within their own #Search like layer?"

I don't think so. Notice that it's all arranged so that id's aren't actually required. The only specifics of any kind are
a) the tagName, DIV, for the dropdown part.
b) the dropdown, and header are direct children of the main container.

This is just the best I could come up with yesterday afternoon. There are certainly better overall approaches.

This is essentially a simple dropdown menu, so it's likely that this could be acheived using just CSS - in standards browsers, maybe with scripted enhancement for IE. A pure CSS approach would give a slightly different behaviour that acts on mouseover, not click.

This one comes to mind for a click-activated menu:
[brainjar.com...]
..but that ceases to operate without script.

Search "CSS dropdown menu" for other options, if you wouldn't mind having something that may "degrade gracefully" without javascript.

----------------------------------

The originating element of an event isn't necessarily the element that has the event handler. The event could have bubbled up from a descendant element.
Finding the originating element, across browsers, is a slightly messy business. That code hopes to clean it up, but is messy itself, partly in the hope of making it more efficient.

STANDARDS

There is a global function,

[b]E[/b]vent
, which is the constructor for event objects.
Event handlers assigned by via script are passed the current event object as the 1st and only argument.

To pass the event in an HTML event handler, you use the keyword(?)

event
.

The originating node is held in the property, target. However, text nodes can originate events, so we need to check the nodeType, and return the parentNode (the actual element) if the nodeType!= 1.

IE

There is a global object,

[b]e[/b]vent
. This holds a reference to the event object of the current, event-driven thread. Otherwise it is null.

Event handlers assigned via script are passed no arguments.

There is no need to pass the event in an HTML event handler, because event is in the global namespace. However, because of this, and the use of the same 'name', the code to pass the event in standards browsers also passes the event in IE.

The originating element is held in the property, srcElement.

--------------

A single function could be used to sort this out. One that has 'branches' in it. At one time, when I was using this approach, I found that my Moz was being a bit sluggish, so the question, "what event model do you use?" is only asked once at load time, so as to cut out that step when the function is used. Javascript functions are objects, so they can be juggled in this way. Some call it "function pointing".

// Does global, Event, exist?
// (as property of window to avoid undeclared variable error)
if(window.Event)
getSrcElement = function(e){standards code}
else
getSrcElement = function(){IE code}

Finally, this

if..else
structure, and the one in the standards function, is turned into a shortcut expression, using the conditional operator [developer-test.mozilla.org]
 

Join The Conversation

Moderators and Top Contributors

Hot Threads This Week

Featured Threads

Free SEO Tools

Hire Expert Members