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.
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.
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.
Name: Collapsible Menu Script
Dynamic content:
<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.
Name: Collapsible Menu Spacer
Dynamic content:
<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…
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.
Hi Mark,
I have tried the given script, As a result I am getting that ‘Expand all’ and two menu sections.
But these menu sections are not related to expand all link.
How can i make these sections visible only when I click on ‘Expand All’.
The sections will always display. It’s the items underneath each section that will be displayed when you click the ‘Expand all’ link.
I am having a similar issue to abhijat. I believe I’ve followed instructions correctly, but the page doesn’t allow the menus to expand or collapse. I am talking about the actual view you see when the page loads, not the sections visible in the CMS editor.
How would it be possible to make the entire heading (h2) clickable to open up the subitem menu, rather than just the little arrow images?
You just have to target the correct HTML element. I haven’t tested this, but I think you could just adjust this line…
collapseImg.down(‘img’).observe(‘click’, toggleSectionItems);
to something like this…
collapseImg.up(‘tr’).observe(‘click’, toggleSectionItems);
Fantastic, thanks so much!
Hi,
This is really great!
However, we already have a theme for ‘cms_menu_vertical_blocks_top_section’ and it’s not compatible with the dropdown arrows it appears – they just disappear.
Is there a way that I could use a different div class?
Thanks,
Madeleine
You should be able to, you just need to adjust my script to target your HTML structure. It’s impossible for me to predict what that might be though so you’ll probably need to work with someone on your side who has a good understanding of Javascript and CSS.