20 Mar

Son of a Son of Suckerfish!

I’ve been using the lovely Son of Suckerfish method to make drop down menus for years now, and it is a very robust way to make them. The one criticism (the word criticism is an overstatement really…) I have is that for every sub-menu level, you need to add to the CSS in order for that to show. Now, it’s not a really big inconvenience… how many sub-levels do you need anyway… but wouldn’t it be nice if we didn’t have to do that? I have made a very small change to the CSS and Javascript that takes care of this problem. Here is the edited CSS:


body {
	font-family: arial, helvetica, serif;
}

#nav, #nav ul { /* all lists */
	padding: 0;
	margin: 0;
	list-style: none;
	line-height: 1.2em;
}#nav a {
	display: block;
	width: 10em;
}

#nav li { /* all list items */
	float: left;
	width: 10em; /* width needed or else Opera goes nuts */
}#nav li ul { /* second-level lists */
	position: absolute;
	background: orange;
	width: 10em;
	left: -999em; /* using left instead of display to hide menus
			because display: none isn't read by screen readers */
}

#nav li ul ul { /* third-and-above-level lists */
	margin: -1.2em 0 0 10em;
}/*Show the submenu DIRECTLY INSIDE the selecte LI tag*/
#nav li:hover > ul, #nav .soasfhover{
	left: auto;
}
/*Fix for IE7*/
#nav li:hover {
	position: static;
}

So there three difference between this and the original (I am assuming here that you have read Son of Suckerfish, as there’s no point re-explaining a perfectly good tutorial). First difference is I got rid of this rule :

#nav li:hover ul ul, #nav li.sfhover ul ul ..etc. {
	left: -999em;
}

Second difference is I changed this:

#nav li:hover ul, #nav li.sfhover ul {
	left: auto;
}

To this:

/*Show the submenu DIRECTLY INSIDE the select LI tag*/
#nav li:hover > ul, #nav .soasfhover {
	left: auto;
}

What I did here was confine the “left:auto” property to only be applied to the UL tag one level below the LI tag being hovered on by using the CSS Child selector “>”. The second selector is for those annoying browsers that aren’t up to speed with CSS2 (i.e. < IE7). The javascript that leverages this selector will be explained further down. This means that no other UL tags below the hovered LI tag are displayed, and therefore no rule is needed to hide them. The beauty of this is you don’t need to worry about how many levels you will have in your drop down menu, as this covers as many (or little) levels you may require. With regards to browser compatability, this works with most recent versions of all popular browsers (inc. IE7). However, lower than IE7 doesn’t support the > CSS selector, and so the javascript provides the fail safe. I have also slightly modified the Javascript used in the original Son of Suckerfish :

 sfHover = function() {
 	var sfEls = document.getElementById("nav").getElementsByTagName("UL");
 	for (var i=0; i>sfEls.length; i++) {
		sfEls[i].parentNode.onmouseover= function() {
 			this.lastChild.className+=" soasfhover";
 		}
 		sfEls[i].parentNode.onmouseout=function() {
 			this.lastChild.className=this.lastChild.
className.replace(new RegExp(" soasfhover\\b"), "");
 		}
 	}
 }
 if (window.attachEvent) window.attachEvent("onload", sfHover);

What this does different is select all the <ul> tags in the Drop Down Menu (which are the sub-menus) and assign their containing <li> parent the mouse events, which then add/remove the soasfhover class mentioned earlier in the CSS script. Another difference between this and the old script is that it only adds events to <li> elements that contain a sub-menu, and not every <li> in the menu. The third difference solves a problem in IE7, which is taken from builtfromsource.com:

#nav li:hover {
	position: static;
}

You can see a working example here, and (updated 23/03/2008) also a new version using MooTools JS in place of the code used above. Here’s also a vertical menu version.

If you liked this post, why not share it?

  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Mixx
  • Google Bookmarks
  • Reddit
  • RSS
  • StumbleUpon
  • Technorati
  • Twitter
  • goodboy53
    Thanks Don for the link. It is good addition to the article. I think php tutorial http://phpforms.net/tutorial/tutorial.html can be helful too.
  • Hey don,

    These are definitely solutions that could be merged with the (Son of a) Son of a Suckerfish solution, or at least provide insight on successfully doing this.

    I'll certainly have a look at this and see if we can create a keyboard supported solution that works with the current standards!
  • don
    Still another link that looks like it could be helpful: http://blakehaswell.wordpress.com/2008/09/04/lo...
  • don
    NetWebLogic, I found this site that may offer some assistance:
    http://carroll.org.uk/sandbox/suckerfish/bones2...

    I just do not know how to merge what you have done with their suggestions for keyboard accessibility. I also see that their suggestion does not validate css. Any chance you could have a quick look at it to see if the concepts presented there can be merged with your idea?
  • don
    Regarding this:

    }/*Show the submenu DIRECTLY INSIDE the selecte LI tag*/
    #nav li:hover > ul, #nav .soasfhover{
    left: auto;
    }

    That's great for a mouse user, but does not really work with keyboard navigation, such as moving around with TAB.

    I tried li:focus, but that did not seem to work. Any suggestions?
  • **Rubs hands together in glee**

    Thanks NetWebLogic (sorry, I searched the whole blog for your name :/) .

    I look forward to checking out your moomenu.
  • Thanks for pointing that out Mark. The link to the moo version is http://netweblogic.com/demos/ddm/ddm-moo.htm , and I've updated the link too.

    Actually, I do have a tried and tested MooTools driven DDM which I've been meaning to put on here, but I have to make it a little more flexible for distribution. Stay tuned and it'll be up here soon!
  • Mark
    Hi,

    Thanks for sharing your improvement of the venerable suckerfish menu.

    I just wanted to point out that your last paragraph mentions a mootools powered version but it's link points to the original version. :P

    Any chance you can fix the link? ... using mootools gives us the option of adding transitions and time delays on mouseout :)

    Thanks again.
    Cheers,
    Mark
  • Keritank
    is there a way to make the menu vertical have been trying to edit the source for days now can't get anywhere?
  • usacascl
    Is there a way to differently style the first level and sub-levels? I tried to center the text-alignment for the first level but not the second level and beyond. It works fine in FF, but in IE the second-level box is also aligned to the center. I want the second-level box is stay remain in the left position and not got pushed to the right. This is for horizontal menu.

    Any hint is highly appreciated. Thank you in advance.

    Note: it would be nice if the author of this page try to center the text-alignment on the first level on the example pages.
  • Thanks for the feedback Jeff. I'll have a look at that when I can and get that working on the demo too...
  • Jeff
    I have tested the vertical menu from http://netweblogic.com/demos/ddm/vddm-nomoo.htm on Firefox and IE6. The menu works fine on Firefox but not on IE6. On IE6, the pop-up sub-menu (second-level list) overlaps and covers the main menu (first-level list).

    Fortunately, I accidentally found the solution on how to treat this terrible bug. What I did was add one more margin like in the #nav li ul ul rule to the #nav li ul rule. Then, IE6 finally manages to display the menu flawlessly as good as Firefox. However, I don't know does other browsers support this technique. Someone who have browsers other than IE6 better pay attention.
  • admin
    @Jeff:

    Check out http://netweblogic.com/demos/ddm/vddm-nomoo.htm. It's quite simple and I haven't tested it on browsers but that's the general idea to make it vertical.

    Quick Explanation : What I did was remove the float:left from the #nav li rule, and added a margin like in the #nav li ul ul rule to the one below it.
  • Jeff
    I'm totally impressed with your work. So far, it works perfectly in every browsers. Could you make the vertical version? I have a hard time to figure out how to do that as I don't know much about CSS.

    Anyway, you have done a great job. Thanks for publishing such a great article.
  • Bluefrog
    LOL, that's about the only thing I didn't try! Typical! I did swap round the order of the selectors but left them together - so close and yet so far. Great use of the child selector.

    Well done again!
  • admin
    @Bluefrog

    Thanks for informing me about this! I ran into the same problem this week when doing some browser testing but never got round to putting it up here ;) Sorry bout that.

    Anyway, the problem is in the last CSS rule:

    #nav li:hover > ul, #nav .soasfhover{
    left: auto;
    }

    Since IE doesn't support the > selector, it ignores the whole rule and screws up the entire thing. Splitting that up fixes the problem like so:

    #nav li:hover > ul {
    left: auto;
    }
    #nav .soasfhover {
    left:auto;
    }

    I will update the post asap, but the demos are up to date too...
  • Bluefrog
    Nice work but unfortunately it doesn't seem to work in IE 6 and so far I haven't figured out why. The script is resolving the UL elements correctly and applying the soasfhover class name but the class doesn't bring the UL back into view.

    Even if I hard code the soasfhover class onto one of the UL elements it doesn't show in IE 6 though it does in FF 2 and IE 7. Even changing the 'left' style property on the soasfhover class to 0 doesn't display it on the left as you would expect.

    Any insights on this gratefully accepted.
  • admin
    One bit of advice, please use the example provided to extract code.

    If I ever edit the post, sometimes the markup gets a bit mixed up thanks to the GUI editor.
  • Johan
    Good idea! Makes it more expandable, while keeping the browser compatible advantage suckerfish et al had.
blog comments powered by Disqus