How to create a JavaScript-based cascading navigation system
Using the ease and simplicity of JavaScript, we create a navigational system, such as a left nav bar, that is defined in a single file.
For sizeable websites, one of the premiere benefits of using linked cascading style sheets (<link rel="stylesheet" type="text/css" href="mystyle.css" />
) is that the style of the entire site is defined in a single file. For example, if it is decided that the font of all text contained in <p>
elements should be Calibri and colored #333, you just need to change one file, such as mystyle.css, and the change cascades throughout the entire site (assuming all such pages link to mystyle.css).
Similarly, for sizable websites where server-side techniques, such as server-side includes, are inconvenient or unavailable, you can use a simple JavaScript-based cascading technique to create a navigational system that is also defined in a single file, such as leftNav.js. When such a file is linked into the pages of a site, all such pages will display the nav system defined in leftNav.js. When you want to make changes to the navigational system, you just change the .js file rather than making changes to the hardcoded markup in every page of the site.
Important Although handy, this technique fails if a user disables JavaScript in the browser. In this scenario, a website using such a JavaScript-based nav would be rendered without the nav system.
To describe this JavaScript-based navigational system, consider the following traditional per page left nav system (which uses a few HTML5 semantic elements):
Traditional left nav
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Traditional Left Nav</title>
<style>
#leftNav {
border-right: 1px grey dashed;
width: 9.5em;
margin-left: -2em;
}
#leftNav ul {
list-style: none;
}
#leftNav ul ul li {
margin-left: -1.5em;
}
</style>
</head>
<body>
<nav id="leftNav">
<ul id="outerUL">
<li id="home"><a href="home.html">Home</a></li>
<li id="portfolio"><a href="portfolio.html">Portfolio</a>
<ul id="portfolioUL">
<li id="thumbnails"><a href="thumbnails.html">Thumbnails</a></li>
<li id="slideShow"><a href="slideShow.html">Slide Show</a></li>
</ul>
</li>
<li id="tagCloud"><a href="tagCloud.html">Tag Cloud</a></li>
</ul>
</nav>
<section>
<!-- Page content here. -->
</section>
</body>
</html>
This markup renders like this:
The goal, then, is to place the left nav’s markup in a JavaScript file and link it into the pages needing a left nav, as shown next:
<body>
<nav id="leftNav"><!-- leftNav.js injected nav system. --></nav>
<section>
<!-- Page content here. -->
</section>
<script src="leftNav.js"></script>
</body>
As you can see, the only remaining nav related markup is the <nav id=”leftNav”>
element itself. The rest of the nav markup is appended to the DOM via the code in leftNav.js.
Fortunately, only two relatively simple JavaScript functions are required to build such a nested nav system:
function appendUl(append_to_id, ul_id) {
var ul = document.createElement('ul');
ul.id = ul_id;
var appendTo = document.getElementById(append_to_id);
appendTo.appendChild(ul);
}
And
function appendLiA(append_to_id, li_id, a_href, a_text) {
var a = document.createElement('a');
a.href = a_href;
a.textContent = a_text;
var li = document.createElement('li');
li.id = li_id;
li.appendChild(a);
var appendTo = document.getElementById(append_to_id);
appendTo.appendChild(li);
}
The appendUl(append_to_id, ul_id)
function creates a <ul>
element whose ID is ul_id
. This <ul>
element is then appended to the element whose ID is append_to_id
. For example, appendUl('leftNav', 'outerUL')
would create <ul id="outerUL">
and append it to the element whose ID is leftNav
. That is, we end up with the following DOM structure:
<nav id="leftNav">
<ul id="outerUL">
</ul>
</nav>
Although slightly more complex, appendLiA(append_to_id, li_id, a_href, a_text)
creates two elements, an <a>
element and an <li>
element. The <a>
element's href
attribute is set to a_href
and its inner text to a_text
. The <li>
element's ID becomes li_id
and contains the <a>
element. The <li>
and <a>
elements are then appended to the element whose ID is append_to_id
. Assuming the prior DOM structure (that is, the previous <nav id="leftNav"><ul id="outerUL">
markup), calling appendLiA('outerUL', 'home', 'home.html', 'Home')
results in:
<nav id="leftNav">
<ul id="outerUL">
<li id="home"><a href="home.html">Home</a></li>
</ul>
</nav>
Now, to build all the following left nav markup (excluding the outer <nav>
element):
<nav id="leftNav">
<ul id="outerUL">
<li id="home"><a href="home.html">Home</a></li>
<li id="portfolio"><a href="portfolio.html">Portfolio</a>
<ul id="portfolioUL">
<li id="thumbnails"><a href="thumbnails.html">Thumbnails</a></li>
<li id="slideShow"><a href="slideShow.html">Slide Show</a></li>
</ul>
</li>
<li id="tagCloud"><a href="tagCloud.html">Tag Cloud</a></li>
</ul>
</nav>
We call these two functions in the following order:
appendUl('leftNav', 'outerUL');
appendLiA('outerUL', 'home', 'home.html', 'Home');
appendLiA('outerUL', 'portfolio', 'portfolio.html', 'Portfolio');
appendUl('portfolio', 'portfolioUL');
appendLiA('portfolioUL', 'thumbnails', 'thumbnails.html', 'Thumbnails');
appendLiA('portfolioUL', 'slideShow', 'slideShow.html', 'Slide Show');
appendLiA('outerUL', 'tagCloud', 'tagCloud.html', 'Tag Cloud');
Be aware that the fourth line in this code example, appendUl('portfolio', 'portfolioUL')
, allows the nesting of Thumbnails and Slide Show:
So to inject a left nav system into any website page, merely include the line <script src=”leftNav.js”></script>
directly before the <body>
tag, as in the following complete example:
Cascading left nav
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Cascading Left Nav</title>
<style>
#leftNav {
border-right: 1px grey dashed;
width: 9.5em;
margin-left: -2em;
}
#leftNav ul {
list-style: none;
}
#leftNav ul ul li {
margin-left: -1.5em;
}
</style>
</head>
<body>
<nav id="leftNav"><!-- leftNav.js injected nav system. --></nav>
<section>
<!-- Page content here. -->
</section>
<script src="leftNav.js"></script>
</body>
</html>
With appendUl
and appendLiA
understood, the remaining contents of leftNav.js are straightforward:
leftNav.js
window.addEventListener('load', leftNav, false);
function leftNav() {
appendUl('leftNav', 'outerUL');
appendLiA('outerUL', 'home', 'home.html', 'Home');
appendLiA('outerUL', 'portfolio', 'portfolio.html', 'Portfolio');
appendUl('portfolio', 'portfolioUL');
appendLiA('portfolioUL', 'thumbnails', 'thumbnails.html', 'Thumbnails');
appendLiA('portfolioUL', 'slideShow', 'slideShow.html', 'Slide Show');
appendLiA('outerUL', 'tagCloud', 'tagCloud.html', 'Tag Cloud');
function appendUl(append_to_id, ul_id) {
/*
Creates a <ul> element whose ID is ul_id. This <ul> element is then
appended to the element whose ID is append_to_id.
*/
var ul = document.createElement('ul');
ul.id = ul_id;
var appendTo = document.getElementById(append_to_id);
appendTo.appendChild(ul);
} // appendUl
function appendLiA(append_to_id, li_id, a_href, a_text) {
/*
Creates two elements, an <a> element and an <li> element. The <a> element's
href attribute is set to a_href and its inner text to a_text. The <li>
element's ID becomes li_id and contains the <a> element. The <li><a>
elements are then appended to the element whose ID is append_to_id.
*/
var a = document.createElement('a');
a.href = a_href;
a.textContent = a_text;
var li = document.createElement('li');
li.id = li_id;
li.appendChild(a);
var appendTo = document.getElementById(append_to_id);
appendTo.appendChild(li);
} // appendLiA
} // leftNav
As you can see in the markup, the only added complexity is the wrapping of appendUl
and appendLiA
by the leftNav
function, and the invoking of leftNav
only after the page is fully loaded via window.addEventListener('load', leftNav, false)
.
With the Important note in mind, you now have the knowledge to build virtually any cascading nav system for your own website(s) by stringing together appropriate calls to appendUl
and appendLiA
, in the correct order, as shown here.