Monday, March 30, 2009 9:50 PM Jan Tielens

Breaking the WSS Top Link Bar in two with jQuery

Since I got a fan of the jQuery Javascript library, usually I can’t resist showing off the power of this library in my SharePoint development courses at U2U. For example two weeks ago I was in sunny Cyprus talking about SharePoint, ASP.NET AJAX and jQuery and I told my students something in the line of “with jQuery you can change pretty much anything in the SharePoint UI by just making use of Javascript”. Promptly one of my students asked me if I could show how to break the Top Link Bar of a SharePoint site into two parts. I really like challenges in my SharePoint courses, but I couldn’t conquer this challenge on the spot, it took me a couple of hours in my hotel room to get this to work. Now that I’ve got some time to polish the code a little bit, I want to share the code with you! :-)

First, let’s talk about the issue that this little jQuery script is going to solve: you probably know if you create subsites in SharePoint sites, those subsites are shown (by default) in the Top Link Bar of the parent site. So the more subsite you’ve got the more items this menu is showing. Life is all good until there are too many items to show in the menu so it’s get too big to fit on the screen. You won’t get an error or something like that of course, but the browser will give the menu the space it needs by adding horizontal scrollbar the page. The screenshot below illustrates this behavior: the top link bar has too many menu items making it pretty hard to access for example the Site Actions menu (you need to scroll to the right).

So how can this be solved with the help of jQuery? Well besides a very powerful DOM Selectors API, the jQuery library also has a DOM Maniplation API. This Manipulation API can change the HTML that’s rendered in the browser, by adding elements, removing elements etc. The idea is to write a Javascript function that adds a second Top Link Bar to the page’s DOM. The easiest way to accomplish this is to just copy the existing Top Link Bar entirely:

$("#zz1_TopNavigationMenu").clone(true).insertAfter($("#zz1_TopNavigationMenu")).attr("id", "zz1_TopNavigationMenuCopy");

This jQuery script will:

  1. $("#zz1_TopNavigationMenu")
    select the element with ID zz1_TopNavigationMenu
  2. .clone(true)
    clone that element
  3. .insertAfter($("#zz1_TopNavigationMenu"))
    insert the cloned element after the original menu
  4. .attr("id", "zz1_TopNavigationMenuCopy");
    set the ID attribute to a new value to be able to identify it uniquely

The result of this function is a page that shows two identical menu bars:

Now the only thing that’s left to do is to remove the unnecessary menu items from both menus:
var nrOfItems = ($("#zz1_TopNavigationMenu > tbody > tr > td").length + 1) / 3;
var splitIndex = (Math.round(nrOfItems / 2) - 1) * 3;
$("#zz1_TopNavigationMenu > tbody > tr > td:gt(" + splitIndex + ")").remove();
$("#zz1_TopNavigationMenuCopy > tbody > tr > td:lt(" + (splitIndex + 1) + ")").remove();

The first two lines calculate the number of items in the menu (each menu item consists of three td elements) and the index of the td element where the table should be “split”. The third line removes all the td elements of the original menu with and index higher than the calculated one. The last line removes all td elements of the copied menu with and index lower than the calculated one.

Now, let’s put everything together and get this to work in a real SharePoint site! The first thing to do is to make sure your SharePoint site loads the jQuery library. This can be done in a couple of ways, for example using the jQuery component of the SmartTools for SharePoint project on CodePlex (read my previous blog posts on jQuery in SharePoint for more info). Secondly the Javascript discussed above should be loaded, and once again there are a couple of ways to do this. For production scenarios I’d recommend to build a Feature using  a delegate control that loads the Javascript function (just like the SmartTools jQuery component does), but for testing you can also do this in a plain Content Editor Web Part (or, God forbid, using the SharePoint Designer tool). So just add a Content Editor Web Part to a page of your SharePoint site, and copy/past the following piece of code in it:

<script>
$(document).ready(function() {
  // Calculate where to split the tables of the menus
  var nrOfItems = ($("#zz1_TopNavigationMenu > tbody > tr > td").length + 1) / 3;
  var splitIndex = (Math.round(nrOfItems / 2) - 1) * 3;
  // Make a copy of the TopNavigationMenu
  $("#zz1_TopNavigationMenu").clone(true).insertAfter($("#zz1_TopNavigationMenu")).attr("id", "zz1_TopNavigationMenuCopy");
  // Remove items from original menu
  $("#zz1_TopNavigationMenu > tbody > tr > td:gt(" + splitIndex + ")").remove();
  // Remove items from copied menu
  $("#zz1_TopNavigationMenuCopy > tbody > tr > td:lt(" + (splitIndex + 1) + ")").remove();
});
</script>

When done, the SharePoint page will now display a Top Link Bar, split into two. Make sure you test the code in a WSS site (a Team Site for example), because Publishing sites (e.g. a Collaboration Portal has another menu, see the remark at the end of this post).

Important remark: the code discussed in this article is just an example for demonstration purposes, if you plan to use this code I strongly recommend to test it in various web browsers and check if it meets your performance goals. The identifiers of the Top Link Bar in the code are uses in WSS sites (e.g. a Team Site), SharePoint Publishing sites generate menus with other ID’s and or structures, so you have to tweak the Javascript. For example: the following code works for a Collaboration Portal using the default.master.

$(document).ready(function() {
  // Calculate where to split the tables of the menus
  var nrOfItems = ($("#zz1_TopNavigationMenu *.zz1_TopNavigationMenu_5 > tbody > tr > td").length) / 3;
  var splitIndex = (Math.round(nrOfItems / 2) - 1) * 3;
  // Make a copy of the TopNavigationMenu
  $("#zz1_TopNavigationMenu").clone(true).insertAfter($("#zz1_TopNavigationMenu")).attr("id", "zz1_TopNavigationMenuCopy");
  // Remove items from original menu
  $("#zz1_TopNavigationMenu *.zz1_TopNavigationMenu_5 > tbody > tr > td:gt(" + splitIndex + ")").remove();
  // Remove items from copied menu
  $("#zz1_TopNavigationMenuCopy > tbody > tr > td:lt(2)").remove();

  // Remove first item (current site) from copied men
  $("#zz1_TopNavigationMenuCopy *.zz1_TopNavigationMenu_5 > tbody > tr > td:lt(" + (splitIndex + 1) + ")").remove();
});

Filed under:

Comments

# re: Breaking the WSS Top Link Bar in two with jQuery

Monday, March 30, 2009 4:58 PM by DigiMortal

Hi Jan!

Is it somehow possible to make those two rows with same width? It reminds tabbed interface in this case. :)

# re: Breaking the WSS Top Link Bar in two with jQuery

Monday, March 30, 2009 7:18 PM by Jaap Vossers

SharePoint & jQuery really is a nice combo!

Good post Jan.

# re: Breaking the WSS Top Link Bar in two with jQuery

Tuesday, March 31, 2009 1:49 AM by Jan Tielens

@DigiMortal: I think this would be possible if you would set the width of the tables explicitly to the same value. This can be done with jQuery as well.

# re: Breaking the WSS Top Link Bar in two with jQuery

Tuesday, March 31, 2009 3:19 AM by Christophe

Breaking the top link bar in two can be useful.

However, this is typically something that can easily be done with plain JavaScript, so it is certainly not a good example of the "power of jQuery".

# re: Breaking the WSS Top Link Bar in two with jQuery

Tuesday, March 31, 2009 5:22 AM by Jan Tielens

@Christophe: Sure jQuery has more stuff than I demonstrated in this post, but I think the selection and modification API's make the script much cleaner and shorter than plain Javascript. Don't you agree?

# re: Breaking the WSS Top Link Bar in two with jQuery

Tuesday, March 31, 2009 5:58 AM by Christophe

Not in this case. jQuery is good for bulk editing and animations, but here we are just splitting in two a string that is clearly identified by its id.

As the objective is to make the links bar fit in a smaller space, how about using the fisheye menu?

# re: Breaking the WSS Top Link Bar in two with jQuery

Tuesday, March 31, 2009 10:39 AM by Jan Tielens

@Christophe: Well, bottom line is that you can do anything in plain javascript, that's available in jQuery. But probably you'd have to write more code.

# re: Breaking the WSS Top Link Bar in two with jQuery

Monday, April 20, 2009 8:13 AM by Cédric

Ok, now for the interesting part.

Using JQuery or JS, this splitted top navigation don’t support second line sub-menu items :

- second line submenus are displayed on the first line

- second line submenus don’t phase out : the last submenu remains on display

So my challenge will be … After this little nice teaser … Is there an easy way, with JS or JQuery, to make this multiline trick working with two or three navigation levels ?

# re: Breaking the WSS Top Link Bar in two with jQuery

Wednesday, May 20, 2009 6:46 PM by Frank

I like that and would like to try it out. I have a site that has 19 sub-sites. We are using WSS 3.0, will this work?  

# re: Breaking the WSS Top Link Bar in two with jQuery

Monday, June 8, 2009 12:19 PM by Mike

If I could find a way to get around issues when using two level drill-down, I would add this to my environment.