Fine Tuning Spry Menu Bars

Fine Tuning Spry Menu Bars

One of the reasons that I started Simple Primate was to have a public space in which to respond to students of my lynda.com titles. I plan on posting answers to some of the most common questions that I receive as well as using this as a platform for diving a bit deeper into more specific issues raised by student feedback. In today’s post, I want to take a closer look at a question sent to me by Maria Osterhoudt regarding Spry Menus. Maria was having a problem with her horizontal Spry Menus in Firefox and was having a hard time tracking down the source of her problems.

Maria had noticed that when testing her Spry Menu Bars in Firefox that the sub-menus would not reset if the user hit the Back button to return to the page. Unlike other browsers which would show the default menu, Firefox would show the sub-menus still in their hover state. Maria checked my example files to see if she had done something wrong and noticed that my menus did the exact same thing. Now, truthfully, most clients might not care about this behavior, but Maria was wondering what the heck was going on and how she could fix it.

So, the first question is why is this happening? Well, since the behavior tends to happen in Firefox and not other browsers, that’s a good place to start. A quick look at Firefox’s caching policy tells us all we need to know:

Firefox 1.5 uses in-memory caching for entire Web pages, including their JavaScript states, for a single browser session. Going backward and forward between visited pages requires no page loading and the JavaScript states are preserved. This feature, referred to by some as bfcache (for “Back-Forward Cache”), makes page navigation very fast. This caching state is preserved until the user closes the browser.

Since that hasn’t changed since 1.5 we can safely say that Firefox’s caching preference is what is causing the issue. Also, it’s worth pointing out that Opera uses Back-Forward Caching as well, so the issue also appears in Opera.

Javascript to the rescue…

Now that we know the problem, solving it should be pretty easy! However, as always, the most obvious solution isn’t necessarily the right one. We could simply prevent the page from caching, thereby forcing the menu to reload each time the page is navigated to. We could do this by using an unload event handler (which forces Firefox to reload the page) or by changing the cache-control property for the page. Although this would work, it would also slow site browsing down and could break pages relying on the cache for functionality, so that’s probably not the best solution.

A better solution would be to look at how the Spry Menubar javascript displays and hides the submenus in the first place, and then to figure out the most efficient means of hiding them from the Back-Button inclined user. Like most Ajax-driven menus, Spry shows and hides submenus by dynamically assigning class names to nested unordered lists based on user interaction. The associated CSS then shows or hides the menus based on the class attributes as well as controlling positioning and formatting for the submenus. By removing those dynamic class names when a link has been clicked, the menus should reset themselves to their default states. Although Firefox has the pagehide event (which would allow us to reset the menus as the user navigates away from the page without resetting the cache) it is proprietary to Firefox. Since Opera also has the same issue, tracking a mouse click would give us more cross-browser compatibility.

I’m a firm believer in not re-inventing the wheel, so the first approach I took was to register a click event listener to the Spry menu and then “piggy back” a mouse click function on the existing mouse out function (which hides the menu). To do this I added the following to the SpryMenuBar.js file:

Spry.Widget.MenuBar.prototype.mouseClick =
Spry.Widget.MenuBar.prototype.mouseOver = function (listitem, e)

This worked fine except for the built in delay which caused the menus to display briefly as the user navigated back to the page. At that point I was trying to decide whether to add some conditional logic to the mouse out function –adding the delay if it detected the mouse out event and removing it if it didn’t– or just writing another function for the mouse click that left out the delay altogether.

Adobe Forums to the Rescue!

It was at this point that I found out someone had already done just that! Checking Adobe’s forums to see if anyone else had taken a stab at the problem (mental note, check the forums first next time…), I found that Niels Hofkes of Solutions In Balance had indeed taken that very approach and written a nice, compact function that adds the mouse out functionality without the associated delay. So rather than write my own, I tried out Niels and found out it worked great.

Essentially, Niels added an mouse click event listener to the menu bar initialization and then took the existing mouse out function, stripped it down to remove the delay, and added it as a new function. It works great and doesn’t cause any weirdness on any of the browsers I tested it on (IE, Firefox, Chrome, and Opera). I’m guessing it will work well on Safari as well, but be sure to test that one as well before releasing this into the wild.

Kudos to Niels for slogging through this and posting about it in the Adobe forums! This solution won’t be for everyone (in fact, some of you may not see this as an issue), and a few of you javascript superstars might have a more efficient solution (if you do feel free to add it to the comments) but for the most part I think this approach works fine. I also think this approach would work equally as well for any Ajax menu bar experiencing the same issues, just find the function that handles the mouse out event and write a similar function for the click handler.

Try it out! Use the menu to browse to About > Contributors and hit the back button. The default should still show the submenus, while the fix should hide the submenus.

Default | Fix

For those of you looking for a more step-by-step approach here’s what you do:

1. Add an event listener to the Spry.Widget.MenuBar.prototype.initialize function. Just below the existing mouseover and mouseout handlers add the following line of code:

this.addEventListener(listitem, 'click', function(e)
{self.mouseClick(listitem, e);}, false);

This adds a click event listener to each item on the menu bar.

2. Next, scroll to the bottom of the page and add the following function:

//remove menu bar hover states when back button is pressed in FF/Opera
Spry.Widget.MenuBar.prototype.mouseClick = function (listitem, e) {
     link = listitem.getElementsByTagName('a')[0];
     var submenus = listitem.getElementsByTagName('ul');
     var menu = (submenus.length > 0 ? submenus[0] : null);
     var hasSubMenu = (menu) ? true : false;
     this.removeClassName(link, hasSubMenu ? 
     ↵this.subHoverClass : this.hoverClass);
     if (menu)
       this.hideSubmenu(menu);
     if (this.hasFocus)
     link.blur();
};

This finds all the menus and submenus and removes the subHoverClass and hoverClass when a link is clicked.

That’s it! I’d like to point out that if this is the result you are always looking for, you’ll need to save this SpryMenuBar.js file and reuse it each time you employ a Spry Menu Bar.

12 Responses to “Fine Tuning Spry Menu Bars”

  1. V1 says:

    Hello,

    Not only Opera has the issue, but also WebKit. So its save to say that all modern browsers have this issue.
    As I suggest in the topic, it saver and wise to use the Spry methods instead of creating a custom piece of code for that.

    If you check the source of the SpryMenu you will see that

    .clearMenus
    .clearSelection

    methods contain the above described functionality. The only part that is missing is removal of the focus of the a element.

    Anyways,

    It was a great read, and a nice bug hunt. :)

  2. jameswill says:

    Thanks V1, and kudos for being my first official comment! Yes, the clearMenus method would work and could easily be triggered by a user click. I agree with Niels however in that testing it I found that top level menu items are harder to clear with this, it’s probably lack of my fine tuning the example and not removing focus. At any rate I appreciate you looking into this!

  3. PhillipSenn says:

    Hey James!
    I’m watching your latest from Lynda.com while eating lunch: Web Design Fundamentals.
    Good stuff!

  4. jameswill says:

    Thanks Phillip! Glad you’re enjoying it, it was a fun course to write!

  5. daveejay says:

    Hello James,

    I’ve been alone in my office with you and your Dreamweaver CS5 Essential Training DVD for over two weeks now. When I emerge to join my family, briefly, for meals and other human needs, they’ve noticed I’ve actually started to pick up your accent. At 57 years old, I don’t suppose it matters much, career-wise, whether I master Dreamweaver or not, but I just can’t quell the urge to know more.

    You’ve played such a big part in the de-mystifying process for me, and I’ve so enjoyed your company, I feel like taking you out for a beer.

    I have a serious question if I may…
    The Spry Menu Bar 0.2 widget, (rebuilt from the ground up) or pixie roll-overs. Which do you recommend?

  6. jameswill says:

    Thanks Dave!
    If I ever get over to New Zealand I’ll take you up on the beer. As for the menu, it depends. If you need a menu that can handle drop-downs and animations, I recommend using Spry or jQuery to power it. For a traditional roll-over menu I’d go with the pixie-background technique (less overhead, no javascript reliance). Of course, there’s nothing preventing you from combining the two!

  7. daveejay says:

    My experience of the spry menu bar, albeit limited, has left me feeling very wary. My drop-downs dropped UP, and some distance to the North in Internet Explorer. That’s why my ears pricked up when I heard the widget had been re-built, but the pixies still look simpler, and so much more trustworthy!

    PS: Can’t honestly say I expected a reply, let alone such a speedy one, as you must be a very busy man. Good on you, mate. It’s great work you’re doing. The beer’s a goer for sure if you’re ever out this way. The world is shrinking all the time.

  8. Brigitte says:

    Hi James
    I am a Graphic Designer with 13 experience in the Print and Publishing Industry. I recently relocated from South Africa to Atlanta, Georgia, and since I don’t have a Green Card yet and am not allowed to work, I am using this time to learn Dreamweaver. I have been diligently following your tutorials on Lynda.com and have gotten so used to the sound of your voice that I feel like I know you. One thing is baffling me though. What do I do if my website is constructed with a main content area and a sidebar but for the portfolio pages I want to do away with the side bar completely to have more room to place my image gallery. How do I achieve this? Do I need a completely separate CSS file or what is the secret? I am completely stuck.

  9. jameswill says:

    Brigitte, it depends on how your pages, and CSS are constructed. Often it might be as simple as leaving out a DIV. More than likely, you’ll need to make an alternate layout in your CSS for a single column layout. This is usually pretty easy to do and can be triggered by a class on the body tag, or a different ID on the content DIVs.

  10. Mahmoud says:

    Hey James,

    first of all , my English is not so good 😀
    It was my dream to speak to you
    Nice to sea your site

    I have one important question i hope you to reply ok ?

    Ok; I’m working in arabic sites and I’d like to use @font-fave tech in one of those , I really tired of using images , and those sites like Cufon and others don’t support arabic language ????

    What to do ??
    What should i read ?? and do ??

  11. oliver02 says:

    Hi, I am reaching out to see if you may have or know of a resource that could guide on how to utilize Dreamweaver code in SharePoint 2007 web parts. I manage the reporting of the R&D Pipeline at Bristol-Myers Squibb and use the CS6 suite of applications for reporting to our Sr. Management. I got most of my skills from Lynda.com watching yourself and Joseph Lowery courses on DW and CSS Navigation. We recently got upgraded to CS6 and have used Dreamweaver to build a simple Spry navigation menu. I have searched all over and can’t seem to find a resource (person/web article/book) that explains how to transfer the DW code and associated CSS to a SharePoint web part. Likely a Content Editor Web Part. My IT team does not support Adobe directly so they can’t assist. I reached out to Adobe but since Spry is not supported functionality in CC, I only get directed to the GITHUB.

    I see at BMS other vendors using Dreamweaver to build flashy navigation which gets loaded to SharePoint sites. Problem is they are web designers contract to build not teach. I would be glad to purchase a book or an article or consulting hour to help bridge the gap we are experiencing.

    Any assistance or direction would be greatly appreciated!

    Best,
    Mimi Bailey

    Mimi Bailey | Associate Director, Reporting and Capabilities
    R&D Operations – Bristol-Myers Squibb
    Route 206 & Province Line Road, Princeton, NJ 08543
    Phone: 609.252.3544; mailto:mimi.bailey@bms.com
    http://www.bms.com

  12. jameswill says:

    Wow,

    Mimi, I’m sorry, but I don’t really know anyone that can help you out. I have stayed away from SharePoint for numerous reasons, so my knowledge isn’t going to help you. I’ll ask around and see if any of my folks know anything.

    Sorry!

    James

Leave a Reply

You must be logged in to post a comment.