Customize the SharePoint Quick Launch Menu using the JavaScript Client Object Model

Customizing the SharePoint Quick Launch menu can be done pretty easily through the UI. This can be accomplished by going to Site Settings > Look and Feel and clicking on Navigation if it is a publishing site, or on Quick Launch if it is not. Both interfaces will allow you to easily add or remove links and order them as you wish. Publishing sites also allow you to inherit navigation from a parent sites and to automatically add and sort individual subsites or pages to the menu. Although adequate for most purposes, customization through the UI is somewhat limited, and you may find yourself needing something more. For example, you might want to show a navigation hierarchy from a site that has a different parent from the current site. The good news is that there is a way to customize the quick launch menu without having to use the UI. By using SharePoint’s Client Object Model, you can construct a query that will return Quick Launch navigation node objects. With a little bit of JavaScript, you can extract a list of link titles and URLs from those nodes and use them as the basic building blocks of a menu tailored to your requirements. Moreover, these navigation nodes are subject to SharePoint security constraints and will therefore already be trimmed for the current user!

The code used to generate a list of navigation nodes is based on functions that are found on an MSDN page that describes how to create SharePoint 2010 client objects, such as lists or subsites. After removing the code related to client object creation, we are left with:

function getNavNodes() {
this.clientContext = new SP.ClientContext(‘/sites/MySiteCollection/MyWebSite’);
this.collNavNode = clientContext.get_web().get_navigation().get_quickLaunch();

clientContext.load(collNavNode);
clientContext.executeQueryAsync(Function.createDelegate(this, this.getChildNavNodes), Function.createDelegate(this, this.onQueryFailed));
}

function getChildNavNodes() {

this.collChildNavNode = collNavNode.get_item(1).get_children();
clientContext.load(collChildNavNode);
clientContext.executeQueryAsync(Function.createDelegate(this, this.createNavNode), Function.createDelegate(this, this.onQueryFailed));
}

function onQuerySucceeded() {

alert(“Created navigation node.”);
}

function onQueryFailed(sender, args) {
alert(‘Request failed. ‘ + args.get_message() + ‘\n’ + args.get_stackTrace());
}

In simplified terms, the above snippet of code can be described as doing the following:

getNavNodes() sets the SharePoint context of operations as starting from ‘/sites/MySiteCollection/MyWebSite’ and gets the top-level Quick Launch menu by making a query. If the query succeeds it calls getChildNavNodes() which then returns an object consisting of a collection of navigation nodes with associated properties.

By using this as a starting point, and after much tooling around and with the invaluable help of my colleague, we arrive at a block of code that can extract the title and URL properties of a list of SharePoint navigation nodes. The example below will return a list of subsite nodes under a parent “/Departments” site. It wraps the function in an ExecuteOrDelayUntilScriptLoaded call because the ClientContext constructor needs SP.js to be loaded before it can create the context. After getting the navigation node, a for loop iterates through the object extracting the title and url of each node and adding it to a list.

ExecuteOrDelayUntilScriptLoaded(function () {
var departmentURL; //HTML code for a single department link
var deptURLlist = [] // array of department links
var deptURLstring; //stringified array of department links for use in HTML code

function getNavNodes() {
this.clientContext = new SP.ClientContext(‘/Departments’);
this.collNavNode = clientContext.get_web().get_navigation().get_quickLaunch();

clientContext.load(collNavNode);
clientContext.executeQueryAsync(
//on query success
Function.createDelegate(this, function(){ _returnParam = succesNavNodes(); }),
//on query failure
Function.createDelegate(this, function(){ _returnParam = onQueryFailed(); })
);
}
//when the query succeeds it calls this function
function succesNavNodes(sender, args) {
//count the total number of links
var itemCount = this.collNavNode.get_count();

//iterate through collection of navigation nodes and extract the title and url properties
for (var i = 0; i < itemCount; i++) {
var title = this.collNavNode.get_item(i).get_title();
var url = this.collNavNode.get_item(i).get_url();

//remove Libraries and Lists from links list if present
if (!(title == ‘Libraries’ || title == ‘Lists’))
{
//build the link in HTML
departmentURL = ‘ ‘ + title + ”;
//add it to the array
deptURLlist.push(departmentURL);
};
}
//stringify the array without commas (join())
deptURLstring = deptURLlist.join(“”)

$(document).ready(function() {
//build your menu here with HTML, JavaScript and jQuery
});
}

function onQueryFailed(sender, args) {
//uncomment below for debugging
//alert(“fail”);
return false;
}
//call the first function to get things started
getNavNodes();
}, “SP.js”);

The above method of creating a list of navigation links contains one drawback – you are limited to creating links within the site collection you are currently running the code against. This is because we are using the JavaScript Client Object Model, which is limited to server-relative URLs when creating a client context. If you want to access the navigation nodes in another site collection or web application, you can do so using the Managed .NET Client Object Model.┬áBut that is a topic for our next post!