A List Apart

Menu
Issue № 183

Dynamically Conjuring Drop-Down Navigation

by Published in CSS, HTML, JavaScript, Interaction Design · 38 Comments

Wouldn’t it be great to allow our users to reach all the pages of our site via a handy drop-down menu — without expending extra effort adding one to each page?

Article Continues Below

We can, by harnessing the powers of a seemingly forgotten HTML element and adding a tap of our JavaScript wand (or a drop of a less magical PHP potion).

The missing link

The LINK element is part of the HEAD section of an HTML document, and provides links to other documents (including style sheets). Some browsers use its data to show a toolbar — alas, the son of the giant of Redmond does not, and his followers are legion.

Let’s teach it what to link

If we want to link to several articles, an appendix, the table of contents, and the first, next, and previous documents, then our HTML might contain something like this:

<link
  rel="StyleSheet"
  href="styles.css"
  type="text/css"
  media="screen"
/>
<link
  rel="StyleSheet"
  href="print.css"
  type="text/css"
  media="print"
/>
<link
  rel="Chapter"
  href="chapter1.html"
  title="Chapter 1"
/>
<link
  rel="Chapter"
  href="chapter2.html"
  title="Chapter 2"
/>
<link
  rel="Chapter"
  href="chapter3.html"
  title="Chapter 3"
/>
<link
  rel="Chapter"
  href="chapter4.html" 
  title="Chapter 4"
/>
<link
  rel="Chapter" 
  href="chapter5.html"
  title="Chapter 5"
/>
<link
  rel="Chapter"
  href="chapter6.html"
  title="Chapter 6"
/>
<link
  rel="Appendix"
  href="links.html"
  title="A. Useful Articles"
/>
<link
  rel="Start"
  href="index.html"
  title="Start page"
/>
<link
  rel="Contents"
  href="toc.html"
  title="Table of Contents"
/>
<link
  rel="next"
    href="chapter4.html"
    title="next: chapter 4"
/>
<link
  rel="prev "
  href="chapter2.html"
  title="previous: chapter 2"
/>

The JavaScript

Our ideal script should be:

  • Unobtrusive. The drop-down menu should only be conjured when it can be populated and linked to the documents.
  • Customizable: We want to define which elements should be added and what their labels and values are — and we want to be able to position the drop-down menu in our pages using CSS and markup rather than JavaScript.
  • Reusable: All we want to do is embed it in the HEAD via a SCRIPT tag

Let’s start with the variables for customization and check to see if the DOM is available.

function showlinks() {
  var elementid='linksnavigation'; 
  var elementtype='div';
  var dropdownlabel='Quick Jump to:';
  var dropdownbutton='jump';
  var dropdownid='linksnavigationdropdown';
  if(
  document.getElementById&&document;.createTextNode
  ) {

Next, we grab any LINK elements (while checking to see if there are any). If there are links, we initialize the variables we use (to avoid overwriting global variables).

  pagelinks = 
    document.getElementsByTagName('link');
  if(pagelinks.length>0) {
    var ispage,
        linksgen,
        pagelinks,
        linksdiv,
        linksform,
        count;
    var linkslabel,
        linkssel,
        linkssubmit,
        newop,
        relatt,
        sel;
    var count=0; 

Then we check to see if the parent element of our drop-down already exists.  If not, we create it.

  if(document.getElementById(elementid)) {
    linksgen=false;
    linksdiv=document.getElementById(elementid);
  } else {
    linksgen=true;
    linksdiv=document.createElement(elementtype);
    linksdiv.setAttribute('id',elementid);
  }

Now it is time to create our form.

  linksform=document.createElement('form'); 
  linkslabel=document.createElement('label')
  linkslabel.appendChild(
    document.createTextNode(dropdownlabel)
  );
  linkslabel.setAttribute('for',dropdownid);
  linkssel=document.createElement('select')
  linkssel.setAttribute('id',dropdownid);
  linkssubmit=document.createElement('input');
  linkssubmit.setAttribute('type','submit');
  linkssubmit.setAttribute('value',dropdownbutton);

To populate the drop-down options, we loop through all the LINK elements and read their title and href attributes. As we don’t want to link to style sheets, we filter them out. Then we compare the href data to the current page to automatically offer the next page as the highlighted option.

  for(i=0;i.getAttribute('rel');
    if(!/sheet/i.test(relatt)) {
      newop=document.createElement('option');
      newop.appendChild(
        document.createTextNode(
          pagelinks<i>.getAttribute('title')
        )
      );
      newop.setAttribute(
        'value',pagelinks<i>.getAttribute('href')
      );
      if(
        document.location.href.indexOf(
          pagelinks<i>.getAttribute('href')
        ) != -1
      ) {
        ispage=count;
      }
      linkssel.appendChild(newop)
      count++;
    }
  } 
  linkssel.selectedIndex=ispage?ispage:0;

Then we need to tell the form what to do once it gets submitted. In our case, we take the value of the selected option and set the window location to this value.

  linksform.onsubmit=function() {
    sel = 
      document.getElementById(dropdownid);
    self.location =
      sel.options[sel.selectedIndex].value;
    return false;
  };

Time to assemble the form.

  linksform.appendChild(linkslabel);
  linksform.appendChild(linkssel);
  linksform.appendChild(linkssubmit);
  linksdiv.appendChild(linksform);

Now we check to see if the linksdiv element has to be created or not, and add it to the start of the document if needed.

      if(linksgen) {
        document.body.insertBefore(
          linksdiv,document.body.firstChild
        );
      }
    }
  }
}

Now we just need to call our script when the page is loaded. For that, we can use window.onload or use a script to add our function to other onload events, bearing in mind that the script option is cleaner, but doesn’t work on some browsers.

  window.onload=showlinks

And that is all. See it in all its glory. (The demo page includes the JavaScript in a text file and a downloadable zip with everything you’ll need to get started.)

But isn’t JavaScript dark magic?

True, JavaScript can be turned off, and we cannot take it for granted. However, we are simply enhancing our document with JavaScript to make up for browser failings.

If we want to offer this functionality without JavaScript, we need to move our efforts to the server side, for example via PHP.

The download pack for this article contains a PHP script that does exactly that. All we need to do is to feed it the HTML  document as a parameter.

Our foo page

This script takes the link data and assembles a form that will be placed either inside the element when it is already there, or directly after the <body> element if it isn’t. The form links to the script itself to send the browser to the chosen page.

38 Reader Comments

Load Comments