Creating a Drop-Down list of Sub-sites in SharePoint 2013 using JavaScript
I created a web part a little while back as a farm solution that would display a drop-down listing of sub-sites that could then be used for quickly navigating to these areas. More recently I decided to revisit the solution and use it as an excuse to brush up on my JavaScript. Be warned, this most definitely isn’t the best code ever and for me it was mostly just a learning exercise.
For the drop-down listing I used a jQuery plugin called Chosen. This plugin is actually a really great example of a jQuery plugin that not only improves the look of the boring old drop-down list but also improves upon its functionality.
The underlying JavaScript code recursively retrieves all sub-sites below the current SharePoint site including those in child sites and displays the results as shown in the screenshots below.
Clicking the drop-down arrow reveals a searchable sub-site listing:
Pressing enter or clicking on a sub-site will automatically navigate you to the root of that site.
Setup
To set this up it should be a pretty straight forward process and involves uploading a few resources and then wiring it all up on a page.
- Upload the required assets for the Chosen plugin to a document library in SharePoint e.g. the Style Library/Site Assets. This will include the following files:
chosen.jquery.min.js – Chosen jQuery plugin
chosen.min.css – Chosen jQuery plugin CSS
chosen-sprite.png – Chosen jQuery plugin – sprite
chosen-sprite@2x.png – Chosen jQuery plugin – sprite retina displays
jquery-1.11.0.min.js – jQuery Library (not required if already included in your masterpage) - Create a new file called getSubSites.html within the same directory and paste in the following code. Note: this code can easily be tweaked and as mentioned previously I’m more than certain that there are ways that this could be improved. Also be aware that it may be necessary to update the script and CSS references to reflect the location within your environment.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091<script type="text/javascript" src="/Style Library/SubSiteDropDown/jquery-1.11.0.min.js"></script><script type="text/javascript" src="/Style Library/SubSiteDropDown/chosen.jquery.min.js"></script><script type="text/javascript" src="/Style Library/SubSiteDropDown/sortSelect.jquery.js"></script><link rel="stylesheet" type="text/css" href="/Style Library/SubSiteDropDown/chosen.min.css"/><script type="text/javascript">// Div class to target and append codevar targetClass = ".list";var dropDownMessage = "Choose a Site...";var clientContext;var sites = new Array();var trackSiteQueries = { "pending" : 0, "complete" : 0 };function getSubWebs(currSubWeb) {// If at the starting site get current webif(currSubWeb == null) {clientContext = new SP.ClientContext.get_current();if (clientContext != undefined && clientContext != null) {var web = clientContext.get_web();clientContext.load(web);clientContext.executeQueryAsync(function(sender, args) {trackSiteQueries.pending++;getSubWebs(web);trackSiteQueries.complete++;});}} else {// Else loop through the subwebs in the current site and recursively get details for each sitevar subWebs = currSubWeb.getSubwebsForCurrentUser(null);clientContext.load(subWebs);clientContext.executeQueryAsync(function(sender, args) {if(subWebs != null) {var webEnum = subWebs.getEnumerator();while(webEnum.moveNext()){var web = webEnum.get_current();var webTitle = web.get_title();var webUrl = web.get_url();var webTemplate = web.get_webTemplate();// Check if the site is a App Web, if so don't add itif(webTemplate != "APP") {sites[webUrl] = webTitle;}trackSiteQueries.pending++;getSubWebs(web);}}trackSiteQueries.complete++;});}}function buildSubSiteList(siteArray) {// Wait until queries of all subsites have been completed then build the drop down listsetTimeout(function () {if(trackSiteQueries.pending == trackSiteQueries.complete) {$(targetClass).append("<select data-placeholder='" + dropDownMessage + "' class='site-dropdown' style='width:350px;' onChange='top.location.href=this.options[this.selectedIndex].value;' ><option value=''></option>");if(siteArray != null) {for(var siteUrl in siteArray) {var siteTitle = siteArray[siteUrl];$(targetClass + "> .site-dropdown").append("<option value='" + siteUrl + "'>" + siteTitle + "</option>");}}$(targetClass).append("</select>");// Sort the list of subsites alphabetically and then trigger Chosen$(targetClass + " > select ").sortSelect().chosen({no_results_text: "Oops, no sub sites found!"});} else {// Continue calling function until queries have completedbuildSubSiteList(siteArray);}}, 50);}$(document).ready(function() {ExecuteOrDelayUntilScriptLoaded(function() {trackSiteQueries.pending++;getSubWebs();buildSubSiteList(sites);}, "sp.js");// Fix to load SP.js - did not appear to be loaded on SP2013 Publishing PagesSP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () { });});</script><!-- Empty div where the code generated by the jQuery is appended --><div class='list'></div> - Create another file called sortSelect.jquery.js and paste in the following (I believe I originally found this jQuery function on StackOverflow but I couldn’t find the original post – Sorry!)
123456789101112$.fn.sortSelect = function () {var mylist = $(this);var listitems = mylist.children('option').get();listitems.sort(function(a, b) {var compA = $(a).text().toUpperCase();var compB = $(b).text().toUpperCase();return (compA < compB) ? -1 : (compA > compB) ? 1 : 0;})$.each(listitems, function(idx, itm) { mylist.append(itm); });return $(this);} - The resulting files should appear in the library as shown below. Make a copy of the URL to the getSubSites.html page.
- Browse to or create a page where you would like to add the drop-down. Edit the page and add a Content Editor web part.
- Click the web part context menu and then select Edit Web Part.
- In the web part property pane paste in Content Link box paste in the URL to getSubSites.html
- Expend the Appearance section and set the Chrome Type to ‘None’
- Click Apply and then Save/Publish the page. After the page reloads you should see the following drop-down list appear on the page. If you have any issues at this point it is probably worth double scheckin gthe script/css references in getSubSites.html
Testing this I found that the sub-site results would get trimmed as expected depending on the users level of access. I also used this on Office 365 and found that it worked without a hitch. Depending on the number of sub-sites it may take a moment or two to load, in this case it would probably be a good idea to show some kind of loading animation while the sub-site data is retrieved.
BadActionDay
Nice! Works like expected. What’s the approach If you would like to display the search box in the homepage, but whant to start search in a second subsite containing the result subsites? Like… http://mainsite [Publish the search box here] – http://mainsite/subsitetoplevel/subsite [Get the search box to start below the SUBSITETOPLEVEL]. Thanks for a great solution!
BadActionDay
Got it. Changed to clientContext = new SP.ClientContext(‘SITE REL URL’) Thanks again!
Tom O'Connor
No worries, glad it was of use!
Tony Platts
For some reason I’m not getting anything at all in the web part. In fact, when I click on the html file itself from within SharePoint this is also blank. Just a white page.
Any suggestions?
Tom O'Connor
Hi Tony,
I would check that all the JavaScript inclusion paths are correct. You can use developer tools to try and diagnose this (F12) and see if any errors are being raised.
Cheers,
Tom
Christophe jacobson
Hi Tom,
could the same be used to generate a list of subsites in a dropdown, when creating a list item.
For example, I have a list of expenses and they are listed in a Sharepoint list. I would like to add a column from which a dropdown would show all the customers in subsites.
Tom O'Connor
Hi Christophe,
You may be able to take the concepts in this post and modify it a bit to get it to work on a list form. You should be able to dynamically populate a choice column with the results you get back from the JavaScript query. You’d need to add this code to your New and Edit form.
Cheers,
Tom
Tony Platts
Ok, another quick question I have.
I have a SharePoint site called A. Site A has the drop down list (and it’s awesome by the way)
I have sites B C D and E underneath site A and they show in the list.
Someone has just created two new subsites under D, let’s call them D1 and D2.
Is there a way I can exclude D1 and D2 from the drop down list on Site A?
I’m happy to manually change the code to specifically exclude D1 and D2 but if there is a way to only look one level down, that would be the preference.
Many thanks for this. I was looking for a solution for a long time and this was the only one I found and it works really well.
Tony Platts
Actually, knowing both ways would be really good if it can be done.