R

ecently I worked on a content management system (CMS) deployment for a customer. One of their requirements was to present a collapsible menu structure on a few pages containing links to catalog items, KB articles, etc. The menu piece is simple. ServiceNow provides several ways to construct CMS menus without requiring any code. The less simple part was making a collapsible menu. In this post I’ll explain the solution I came up with to provide a collapsible menu system that leverages the existing menu system in the ServiceNow CMS.

Collapsible Menu Page

The approach I had seen taken in the past with collapsible menus was to leverage some sort of jQuery plugin to pull in the menu items from the database. I considered this approach, but wanted something that was closer to the existing menu setup. I really didn’t want to have to include jQuery and deal with the extra burden of a completely separate library just for this purpose.

What I came up with is a fairly simple solution that overlays the existing menu HTML of a ‘Vertical Blocks’ menu with some javascript that manages the expand/collapse functionality.

Setting up the menu(s)…

The first step is to set up your menus. The only important part of this step for this solution is to make sure your menu is a VERTICAL BLOCKS menu. Other than that, simply create the menu, add sections and items below it, and include any icons you want to use. If you want to have a page with a 2-column menu setup, you’ll need to create a separate menu for each column.
Navigation Menu

Creating the content blocks…

Once you’ve created the menu(s) you need to create a dynamic content block that contains the code to add expand/collapse functionality to the menu. The code below can be used as-is in a dynamic content block in your system.

‘Collapsible Menu Script’ Dynamic Content Block
Name: Collapsible Menu Script
Dynamic content:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">

<table cellpadding="5">
   <tbody>
      <tr>
         <td id="expandAllImg" onclick="showSectionItems();"><a><img src="images/icons/nav_expandall.gifx"></img><b> Expand all</b></a></td>
         <td id="collapseAllImg" onclick="hideSectionItems();" style="display:none;"><a><img src="images/icons/nav_collapseall.gifx"></img><b> Collapse all</b></a></td>
      </tr>
   </tbody>
</table>

<script>
Event.observe(window, 'load', function() {
  var sections = $$('tr.cms_menu_vertical_blocks_top_section');
   sections.each(function(s) {
      //If subitems
      if(s.next()){
         //Hide the subitems and add the collapse image
         s.next().hide();
         var collapseImg = s.down().insert({
            top: '<a><img src="images/arrows_expand_sm.gifx" alt="Expand" title="Expand" class="collapse_menu_icon"></img></a>'
         });
         collapseImg.down('img').observe('click', toggleSectionItems);
      }
   });
});

function toggleSectionItems(event) {
   var element = event.element();
   if(element.getAttribute('title') == 'Expand'){
      element.title = 'Collapse';
      element.src = 'images/arrows_collapse_sm.gifx';  
      element.alt = 'Collapse';
   }
   else{
      element.title = 'Expand';
      element.src = 'images/arrows_expand_sm.gifx';  
      element.alt = 'Expand';
   }
   element.up('tr.cms_menu_vertical_blocks_top_section').next().fadeToggle();
}

function showSectionItems() {
   var sections = $$('tr.cms_menu_vertical_blocks_top_section');
   sections.each(function(s) {
      //If subitems
      if(s.next()){
         s.next().fadeIn();
      }
   });
   $('collapseAllImg').show();
   $('expandAllImg').hide();
   $$('.collapse_menu_icon').each(function(i) {
      i.title = 'Collapse';
      i.src = 'images/arrows_collapse_sm.gifx';  
      i.alt = 'Collapse';
   });
}

function hideSectionItems() {
   var sections = $$('tr.cms_menu_vertical_blocks_top_section');
   sections.each(function(s) {
      //If subitems
      if(s.next()){
         s.next().fadeOut();
      }
   });
   $('collapseAllImg').hide();
   $('expandAllImg').show();
   $$('.collapse_menu_icon').each(function(i) {
      i.title = 'Expand';
      i.src = 'images/arrows_expand_sm.gifx';  
      i.alt = 'Expand';
   });
}
</script>
</j:jelly>

If you are setting up a 2-column menu page you’ll also want to create a spacer content block so that your menus line up correctly on the page.

‘Collapsible Menu Spacer’ Dynamic Content Block
Name: Collapsible Menu Spacer
Dynamic content:

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
   <div style="margin-top:27px">
   </div>
</j:jelly>

Setting up the page

Finally, you can set up the actual CMS page containing your menu and the content block (or blocks if you’re doing a 2-column menu). The page setup should look something like the screenshot here…
Collapsible Menu Page Setup

If you’ve set everything up correctly you should have a nice menu page with collapsible menus! Each menu section can be expanded individually to show the items underneath it. You can also use the ‘Expand all/Collapse all’ toggle to show and hide all items under all sections on the page.
Collapsible Menu Page