Customizing the SharePoint 2010 Menu with CSS and jQuery

SharePoint 2010 offers significant changes to the Web Content Management (WCM) facet of the platform.  A common goal for this version is to provide better HTML markup,  as well as easier skinning and themeing support for public Internet web sites.  While it was possible to create such sites in the previous version of SharePoint, it yielded bloated HTML with little support for web standards compliance and search engine optimization (SEO), and often required a certain degree of web trickery to make it visually appealing.

In most websites, a common UI component is a navigation menu which provides hierarchical links with dynamic animations.  In SharePoint, we have The AspMenu control, which has been around since MOSS 2007 and has its roots in the ASP.NET Menu control.  The drawback of such server control was the lack of ability for a web “devigner” (developer * designer) to control its rendered HTML markup.  Well, fear no more!  SharePoint 2010’s AspMenu has been revamped to include better support for CSS and HTML unordered lists (goodbye, nested tables).

There’s a new boolean property in the SharePoint AspMenu control named UseSimpleRendering, which is set to true by default in the Nightanday.master master page (the out-of-box master page for WCM sites using the Publishing features).  This “magical” property overrides the default rendering of the ASP.NET Menu control, and yields beautiful HTML in the form of nested unordered lists with CSS classes that can be easily styled.

Recently, I was challenged to style a SharePoint menu for a public website in order to provide custom images for each menu item, as well as rounded corners for the dynamic flyout panels – all that you might expect in a “HTML5”-like website.  As some of you readers might know, the behavior of the menu is to render text hyperlinks of each menu node based on a Site Map Provider.  So, how do we change the menu from text to a custom icon for each node?

jQuery and CSS to the Rescue!

In order to achieve the desired behavior, we establish a convention that takes the text of each static item on the menu and makes it a CSS class name that can be styled**.  In other words, if you have the following:

  • static item 1
  • static item 2
  • static item 3

Then you would have the following corresponding CSS classes in your stylesheet:
.staticitem1 { ... }
.staticitem2 { ... }
.staticitem3 { ... }

**note: some of your static menu item text strings might have non-alphanumeric characters, such as “&” and “?”.  Don’t worry, we’ll account for that in our JavaScript, later on.

Now, we’ll resort to jQuery to select the appropriate DOM elements for the static menu items, retrieve the text, and add a CSS class to the static item to have its background “painted” with the image corresponding to the menu item.  We start off with the default HTML markup similar to this:

<ul>
    <li class="static dynamic-children">
      <a class="static dynamic-children menu-item" href="http://www.yahoo.com">
        <span class="additional-background">
          <span class="menu-item-text">static item 1</span>
        </span>
      </a>
      <ul class="dynamic">
        <li class="dynamic">
          <a class="dynamic menu-item" href="/">
            <span class="additional-background">
              <span class="menu-item-text">Dynamic Item 1.1</span>
            </span>
          </a>
        </li>
      </ul>
    </li>
    <li class="static">
      <a class="static menu-item" href="http://www.google.com">
        <span class="additional-background">
          <span class="menu-item-text">static item 2</span>
        </span>
      </a>
    </li>
</ul>

Given the above DOM structure, we can write some JavaScript and use jQuery to inject our CSS at load time for each static item in my menu.  In order to make our jQuery selectors more efficient at finding the menu, I configured the AspMenu’s CssClass property with the value “my-menu”.  This simply forces the menu to render wrapped in an element decorated with this CSS class, which we can leverage as the starting point in the selector.  Here’s the script:

$(function(){
  $('.my-menu li.static').each(function(){
        var bkgdSpan = $('span.additional-background:first', this),
            textSpan = $('span.menu-item-text', bkgdSpan),
            itemName = textSpan.text()
                               .replace(/\?/g,"")
                               .replace(/\&amp\;/g,"&")
                               .replace(/\&/g,"and")
                               .replace(/ /g,"");
        bkgdSpan.addClass('mymenu-staticitem-image ' + itemName);
        textSpan.text('');
  });
});

Let’s break the above code down:

1) find every list item with a CSS class “static” who is a descendant of element with class “my-menu”
2) for each of these items, find the following:
– the first SPAN element with  class “additional-background”,
– SPAN elements with class “menu-item-text” inside the “additional-background” SPAN
3) parse the text value from the “menu-item-text” span and remove characters”?”, “&”, and empty space
4) add 2 new CSS classes to the “additional-background” SPAN: “mymenu-staticitem-image”, and another class derived from the text value in the span element itself.  THIS IS THE KEY TO GETTING THE CORRESPONDING IMAGE FOR THE MENU ITEM.
5) finally, since we are replacing the text string with an icon image for the static item, we remove the text content from the “menu-item-text” SPAN.

At this point, all you have to do is setup your CSS classes with the appropriate images for each static item and deploy your masterpage, css, and JavaScript changes to your SharePoint environment.  Here’s a sample CSS snippet to style the menu above, and include several other style rules to further modify the default menu:

/* reset some styles for cross-browser consistency */
.my-menu ul, .my-menu li, .my-menu div, .my-menu span, .my-menu a {
   margin: 0;
   padding: 0;
   border: 0;
   font-size: 100%;
   font: inherit;
   vertical-align: baseline;
}
/*** end reset ****/

.left-nav-bar .my-menu,
.left-nav-bar .ms-quicklaunchouter {
   font-size: 8pt;
   line-height: 1.25em;
}

/* item style */
.my-menu li.static > .menu-item {
   border-collapse: collapse;
   border-bottom: 1px #336666 dotted;
}

/* item style hover */
.my-menu li.static > a:hover { background: url(/images/nav_blue_bkgd.png) no-repeat center right; }

/* current selected static item */
.my-menu li.static .selected{ background: url(/images/nav_blue_bkgd.png) no-repeat center right; }

/* dynamic flyout holder */
.my-menu ul.dynamic {
   background-color:white;
   min-height: 85px;
   padding: 0 5px 0 5px;
   border-top: 1px #2e72bb outset;
   border-right: 1px #2e72bb outset;
   border-bottom: 1px #2e72bb outset;
   z-index:100;
   top: -15px !important;
}

/* dynamic flyout item */
.my-menu li.dynamic > .menu-item {
   background-color: white !important;
   padding: 10px 10px 10px 10px;
   border-bottom: 1px #336666 dotted;
   white-space:nowrap;
}

/* dynamic flyout item links */
.my-menu li.dynamic > a, .my-menu li.dynamic > a:visited{
   font-size: 8pt;
   color: #003366;
}

/* flyout item hover */
.my-menu li.dynamic > a:hover { color:#001428; }

/* dynamically added to static items via JS - to show correct CSS sprite image */
.mymenu-staticitem-image {
   background: url(/images/menuSprites.png) no-repeat right !important;
   position: relative;
   vertical-align:middle;
   width: 212px;
   height: 72px;
   border-bottom: 0px;
   right: -7px;
   top: 1px;
}

/* these classes are derived from the text string for each static menu item */
.staticitem1 { background-position: 0px 0px !important;}
.staticitem2 { background-position: 0px -73px !important;}

That’s pretty much all you need to transform you plain text SharePoint AspMenu into an eye-catching, CSS-styled menu with custom images.  Now, you must be thinking, ‘Wait a minute…at the beginning of the article, you said “and add rounded corners”‘.  Ah…yes, rounded corners for the dynamic menu panels.  This very neat effect is fully supported by CSS3’s new border-radius property.  However, older browsers (such as IE8 and older) do not implement CSS3, and thus we have to resort to a jQuery plugin to do the trick and degrade gracefully to good ol’ square corners for such browsers.

There are several different jQuery plugins that achieve this, but I have found Malsup Corners plugin to work best (http://jquery.malsup.com/corner).    Once you’ve deployed the plugin script file to SharePoint, just add the following line to your jQuery Ready function body:

$('.my-menu ul.dynamic').corner("keep");

That’s it!  We have successfully customized the new SharePoint 2010’s AspMenu using CSS and jQuery.  Here’s what mine looks like:

customized SharePoint AspMenu with images and rounded corners

customized SharePoint AspMenu with images and rounded corners