Single-level Dropdowns
Right. Let's not beat around the bush. The initial HTML we're dealing with will look something like this:
<ul id="nav">
<li><a href="#">Percoideia>
<ul>
<li><a href="#">Remorasa>li>
<li><a href="#">Tilefishesa>li>
<li><a href="#">Bluefishesa>li>
<li><a href="#">Tigerfishesa>li>
ul>
li>
<li><a href="#">Anabantoideia>
<ul>
<li><a href="#">Climbing perchesa>li>
<li><a href="#">Labyrinthfishesa>li>
<li><a href="#">Kissing gouramisa>li>
<li><a href="#">Pike-headsa>li>
<li><a href="#">Giant gouramisa>li>
ul>
li>
ul>
A good wholesome structured unordered list.
To set things up we need some basic styling:
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
}
#nav a {
display: block;
width: 10em;
}
#nav li {
float: left;
width: 10em;
}
Note that you need to specify a width in the
#nav li
selector or else Opera will chuck a wobbly. Also remember that because we're floating things, the content underneath the dropdowns also needs to be cleared (clear: left
).We obviously need to hide the lists that we want to 'drop down' but to make things as accessible as possible we need to avoid using
display: none
, which, as is commonly mentioned in image replacement write-ups, hides elements from some screen readers. You might think that there are a multitude of ways to deal with this, but having exhaustedly experimented with widths, heights, margins, top and clip across a large number of browsers, the best solution (accommodating multiple level lists anyway) lies in manipulating the left
property.The CSS specs say that
top
, right
, bottom
and left
values should offset an absolutely positioned box from its containing block. But unfortunately Opera decides to offset absolutely positioned boxes in relation to the page and that's why the original Suckerfish Dropdowns didn't work on Opera - because they relied on the top
and left
properties with explicit lengths.So instead of
display: none
we use left: -999em
to propel the dropdown list out of view and then left: auto
(rather than left: 0
) to bring it back:
#nav li ul {
position: absolute;
width: 10em;
left: -999em;
}
#nav li:hover ul {
left: auto;
}
And that will sort out everything for those browsers that fully support the
:hover
pseudo class, but for Internet Explorer we need to set the Suckerfish JavaScript loose:
sfHover = function() {
var sfEls = document.getElementById("nav").getElementsByTagName("LI");
for (var i=0; i) {
sfEls[i].onmouseover=function() {
this.className+=" sfhover";
}
sfEls[i].onmouseout=function() {
this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
}
}
}
if (window.attachEvent) window.attachEvent("onload", sfHover);
Basically, this applies the 'sfhover' class to
li
elements in the 'nav' id
'd ul
element when they are 'moused over' and removes it, using a regular expression, when 'moused out'.So now we've got the Suckerfish pumping out new classes, the next step is to simply duplicate the
:hover
selector with 'sfhover' class selectors:
#nav li:hover ul, #nav li.sfhover ul {
left: auto;
}
And there you go. Your standard single-level dropdown menu.
Multi-level Dropdowns
The original Suckerfish Dropdowns article covered only single-level dropdown menus, but with a bit of an extension of the cascading logic, it is quite possible to create multi-level dropdowns with CSS too. And, unlike the original Suckerfish JavaScript code, the 'sfHover' function now applies the behaviour to all of the descendent
li
elements of 'nav' rather than just the direct children so now multi-level dropdown menus are quite possible in Internet Explorer too.So, to get started, let's say we're dealing with a list structure with more levels like this:
<ul id="nav">
<li><a href="#">Percoideia>
<ul>
<li><a href="#">Remorasa>
<ul>
<li><a href="#">Echeneisa>li>
<li><a href="#">Phtheirichthysa>li>
<li><a href="#">Remoraa>li>
<li><a href="#">Remorinaa>li>
<li><a href="#">Rhombochirusa>li>
ul>
li>
<li><a href="#">Tilefishesa>li>
<li><a href="#">Bluefishesa>li>
<li><a href="#">Tigerfishesa>li>
ul>
li>
<li><a href="#">Anabantoideia>
li>
ul>
There are a few things we need to add to the single-level method. Firstly, the third-level list (in this example 'Echeneis, 'Phtheirichthys' etc.) needs to drop down to the side of the corresponding list item (in this case 'Remoras'), so we need to add this rule, which will apply to all dropdowns after the first one:
#nav li ul ul {
margin: -1em 0 0 10em;
}
Because we can't explicitly specify the top of the absolutely positioned boxes, they will sit below the line of the hovered list item, which is why the top margin of the next level of lists needs to be set to
-1em
. But this won't pull the menus up far enough the be in line with the corresponding list item because by default line heights are greater than 1em (usually 1.2em), so we need to add a little something to the initial ul
rule set:
#nav, #nav ul {
padding: 0;
margin: 0;
list-style: none;
line-height: 1;
}
Due to the cascading effect whereby upon the second level list being displayed, the third level list would also be revealed, we also need to explicitly hide that third level list (remember that we need to duplicate the
:hover
pseudo class with the .sfhover
class):
#nav li:hover ul ul, #nav li.sfhover ul ul {
left: -999em;
}
Now, this rule can be contradicted so that it is displayed when the corresponding list item is hovered over by expanding on the displaying of the dropdown (which with the single level dropdown was
#nav li:hover ul, #nav li.sfhover ul { left: auto; }
):
#nav li:hover ul, #nav li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul {
left: auto;
}
And that will establish a solid two level dropdown menu.
Following the same logic you can accommodate as many levels of dropdown menus as you want:
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul {
left: -999em;
}
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul {
left: auto;
}
And in the crazy event you need four levels:
#nav li:hover ul ul, #nav li:hover ul ul ul, #nav li:hover ul ul ul ul, #nav li.sfhover ul ul, #nav li.sfhover ul ul ul, #nav li.sfhover ul ul ul ul {
left: -999em;
}
#nav li:hover ul, #nav li li:hover ul, #nav li li li:hover ul, #nav li li li li:hover ul, #nav li.sfhover ul, #nav li li.sfhover ul, #nav li li li.sfhover ul, #nav li li li li.sfhover ul {
left: auto;
}
Examples
So you might have already looked at the one level, two level and three level bare-bones examples, which are probably the best places to take a look at the uncluttered source code in action, but, of course, you can make things look a bit prettier. You could even turn it into a vertical menu rather than a horizontal one.
No comments:
Post a Comment