Dynamically Conjuring Drop-Down Navigation

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#section1

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#section2

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#section3

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?#section4

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

  1. This is a pretty cool technique, although I don’t quite see the difference between doing everything via LINK attributes, rather than having a generic dropdown-list form element.

    Yes, many browsers do use LINK attributes to create custom button bars for that page. This makes it awkward to have a LINK list that is very long. In your demo, it’s a dropdown list, an infinitely high widget. A toolbar is limited in both width (width of the window) and has a height of a single element.

    Often in the article, it is stated that you can hav every page on your site as a LINK attribute. This is less-than-optimal, as text browsers tend to place the contents of the LINKs at the top of the page, and “normal” browsers could potentially have a giant, confusing, non-hierarchical list from which to pick.

    Perhaps a better way to go about this would be to dynamically generate LINK attributes for previous/next, but leave the full site-list out of the equation. This will be a bit more meaningful, as it provides LINKs in context with the current page

  2. “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 addding [sic] one to each page?”

    So instead you’ve gone ahead and created a lengthier and convoluted method to do what the FORM/SELECT elements (and a little JS) already do. I would still have to add all the LINK elements to “each page”, and when it comes time to update it, it’s still as intrinsically time-consuming as updating FORM/SELECT, so where is the benefit? So you’re hard-coding LINK elements now instead of OPTION. Nice use of JavaScript/DOM, but I don’t see the need. Have I missed something?

  3. >>I would still have to add all the LINK elements to “each page”

    Not if they’re in a SSI (or other flat file that gets flown in).

    If they’re in an include, when it comes time to update, you just change the links in that one file.

    No?

  4. If you’re going to use SSI to include the link elements, then you could still also use the form/select method and use an include there instead…and then you don’t need to use javascript…or am I missing something?

  5. This is only an enhancement to the navigation, not a replacement. A site using this method should still be usable and accessible.

  6. I think most of the points were said before already, but one.

    About the php solution, I did not look at the php source, but giving the page to be parsed as a part of an url.. ..seems like a security risk to me.

    Please tell me that you have taken care of stuff like: page.php?p=/etc/pwd

  7. As Michael pointed out, this is simply to show what is already there, only lacking support in some browsers. Therefore it is very accessible, and as assistive technology _does_ support LINK, a lot better than just a simple dropdown.

    As for the “turn off Javascript and the navigation is off”, please read the end of the article, which points to a PHP solution for the same effect.

    As for the PHP solution, be free to improve it, currently it either checks if the value was sent to it via post and sets the location to it, or, if there is a P via get, it checks if the file exists, and loads it via fgets. I presume therefore that it will fail with something like /etc/pwd.

  8. I had to agree with the other readers questioning the usefulness of this technique – it does seem like a case of ‘if it ain’t broke, don’t fix it’… with one caveat. I actually have a client right now whose site doesn’t have PHP. They have a relationship with their host that they really like, despite the fact that the host is pricy, and they don’t want to move. So I had to work around not being able to do any SSI options. This could have been a very useful technique when I was revamping their site.

    But in most cases, this seems likely to be more trouble than it’s worth.

    just my $.02…

  9. PHP is not necessary, it is just needed if you want to offer the same functionality without Javascript. LINKs are there to link documents together, they should not replace a full web site navigation though. The original version of the article might have explained this concept a lot more, but was too lengthy to be released on ALA.

    LINK elements are good, for accessibility: (http://www.w3.org/TR/WCAG10-HTML-TECHS/#document-meta), they might help you with SEO (http://www.seoconsultants.com/meta-tags/link-relationship.htm) and on some browsers like Mozilla they also mean the next pages get preloaded (http://www.mozilla.org/projects/netlib/Link_Prefetching_FAQ.html). Link can be a lot of different things (http://www.w3.org/TR/REC-html40/types.html#type-links) not only site navigation. The idea of this article is that you should use LINK anyway, as it does make a lot of sense for linked documents. A lot of designers don’t care about it though, or even don’t know about it, mainly because IE does not display them. This is the gap this script should fill.

  10. Why a dropdown list? Why not use an (un)ordered list in which the list items are good old hypertext links?

    Also, don’t the 8 PHP search&replace’s make the script slow?

    Using XML/XSLT here seems like a wonderful idea to me. An XML sitemap could easily be transformed into a series of LINK elements, as well as a normal list.

  11. anon, yes, both valid points. I chose the dropdownnot to waste much screen estate. The regexp are not much of performance issue, after all, this is executed on the server.
    As for PHP, again, this is a fallback, if you want to use any other technique, be my guest.

    If you can use includes, you can store your link data in PHP in a database or associative array and automatically populate prev and next, too, by reading the current page name.

    I used this technique in my unobtrusive Javascript course in case you want to see an example. I wanted users to go through it page by page, but have a chance to shortcut, should they come back later:
    http://www.onlinetools.org/articles/unobtrusivejavascript/

  12. No need for this script if you use Opera or Mozilla! They display the navigation bar for you to click on. Great idea though – you could have something big here. How about improving it way beyond what the aforementioned browsers can do?

  13. Drop down nav.

    If you have 30+ pages on a website then a drop down list can be a bit hard to wade through. As navigation I don’t like drop down menu’s that much. They can be powerful in some instances though such as a links page. I use several drop downs on my own links page which loads pages into a frame. Quite handy there but not too useful for nav otherwise.

    If you are going for a list all approach I think CSS/dhtml techniques are better and more usable I believe especially with sub menus.

    Drop downs might be handy if you are using sub index pages where not all links are 1 click away but a simple site map page can do the same job whilst saving a lot of code on every page.

  14. true, and your point is? Again: This is not to replace a navigation of a web site, we talk about linked documents here, which have a common theme, much like tutorials and online books. LINK is not a navigation replacement, it is above the site navigation, a structural meta navigation.

  15. if I have to type out all that js to render a two option dropdown it doesn’t seem very productive. I still can’t exactly tell where the ‘dynamic’ even comes into play after reading it 3x over ? anyone care to explain ?

    How i’ve handled this problem is server-side using php. I read the directory I specify in and grab all files with a .html and dynamically generate a dropdown box on a loop using the list of contents from the dir scan. Now that is dynamic [and less lines of code than this JS in the article]…. and server side which leaves no chance of the user being able to interfere by turning a browser preference off.

  16. I forgot to say that I see how it could be more useful from an accesibility standpoint but I don’t see the dynamic part ?

  17. Well, kudos to you, you found yet another way to do the same.
    I also take in your point about dynamic, as it is a misnomer in this case. The dynamic bit is the generation of the dropdown from data in the document, data that otherwise would only be there for clients that support LINK. IE for example does not use the link data we put in for anything, which means a lot of developers don’t even bother adding this data.

    Dynamic in your sense means that the link data and the navigation gets generated for each document server side by reading all documents and also setting prev and next, which is exactly what you should do and is a great idea. This article, above all, was meant to raise awareness for the LINK element. Some of the original explanations and intensions got lost in the editing process though.

    However, your point about “all these lines of code” you need to “type in”, is a meek one, as all you need to do is to call the script in the zip via ONE script tag. The script can also be done in 20 lines less, but is less legible and worse as a code example then.

  18. Nice use of the LINK tag. I wish we could use more standard HTML elements than just the
    “strangeset” IE implements. Maybe as IE becomes less used. I’ve got my Mom using Mozilla. It starts at home. Good job Chris.

  19. Peter-Paul Koch has used this technique for some time on his own site to create a few unobtrusive links at the bottom of each page. He also uses his sitemap as the navigation for his entire site (in a frame not an SSI) using javascript to make it into a dhtml menu. His work should probably have been credited here, and is likely better written.

  20. Interesting, didn’t know that. I just went through his script and saw what you mean:
    var y = document.getElementsByTagName(‘link’);
    var links = ”;
    for (var i=0;i‘ + y[i].getAttribute(‘rel’) + ‘‘;
    }
    links += ‘
    ‘;
    links += ‘<a href=”/home.html”>home</a>’;
    links += ‘<a href=”/sitemap.html”>sitemap</a>’;
    links += ‘<a href=”/contact.html”>contact</a>’;
    links += ‘<a href=”/about/copyright.html”>copyright</a>’;

    However, I was not aware of it and unless you read the “making of quirksmode” article on the site you wouldn’t be either. So let’s be a bit more careful assuming that some ideas were taken from somewhere else, shall we?

    Personally I am not a big fan of writing out HTML without generating it via DOM, but that is just me.

  21. I have a website that must be accessible for non Javascript users, yet I want Javascript users to navigate the site usings a drop down menu (with Javascript redirection).

    With this technique non-Javascript users do not have to see the (then useless) menu and only see the textual links I’ve placed in a noscript tag.

    My concern: does it work in IE5 (DOM support) ?

  22. We are talking about two different things here. This technique is there to show accessibility enhancing LINK info for IE. A Javascript Navigation can be made accessible by simply making it non-dependent on Javascript but enhance it by it.

    It works in IE5, you can test it by downloading the standalone IE5 versions out there on the web.
    http://www.skyzyx.com/archives/000094.php

    You can make a dropdown with javascript redirection accessible by applying the javascript onsubmit rather than onchange on the select. All you need is a Javascript fallback on the server side.
    http://www.evolt.org/forms_javascript/

  23. I’m posting from Northern Ireland right now so I won’t say too much…

    Opera 7 already has this feature. But maybe you should use onchange for the form instead of a button.

  24. And yes, as stated “Some browsers use its data to show a toolbar — alas, the son of the giant of Redmond does not, and his followers are legion.”

  25. 1. some good ideas on using existing markup in other ways

    2. every idea at ala (and elsewhere) is to be built upon and explored (it still humors me to see the ‘I already did that’ post, or ‘ I can do one better’… …that’s great…

    but, man, sometimes these discussions make you not wanna write another article, huh?

    so, nothing but thanks for your time and effort trying to help people.

    [undisclosed location]
    peace
    z

  26. Dante, as pointed out by Chris, using onsubmit on a button rather than onchange on the drop-down menu is the accessible option. The reason for this is the browser will only go to get a page when the _user_ confirms they want that to happen.

    If the user had limited mobility with a pointing device they may accidentally change the drop-down menu to an option they actually do not want. Or, maybe the user is negotiating the drop-down menu using the keyboard: they would use arrow keys to select the option they want and would then tab to the submit button.

    🙂

  27. Cheers, that was the reason. It is negotiable though if users who _rely_ on keyboard navigation don’t actually know that a dropdown onselect is accessible when you pressl alt+arrow down first to expand it and then choose by using arrow up and down and submitting with enter.

  28. Lynx, and maybe other text browsers, display the links at the beginning of the document. If you include too many, it takes a long time to tab past them all to get to any links in the content.

    A workaround is to make the first point to an anchor at the beginning of the content, effectively skipping the links.

    My site uses next, prev, up and start “special” links extensively, with a “control bar” in the header to allow those without Opera/Mozilla to access them.

  29. What is a BFU? And let’s not forget that a lot of alternative User agents _do_ support LINK to start with, and the dropdown only appears when DOM is available. This means that the chances of a User agent creating the dropdown not being able to render it is very slim.

  30. i think this is a really cool idea, but the main problem i have with it isn’t the HTML bloating or the non-javascript browsers and whatnot. My question is: don’t browsers preload anything within a link attribute? anything in a link tag will be preloaded because of the html DOM. if there’s a way to turn off that option in the HTML somehow, that would make this much more worthwhile for me. i personally wouldn’t want to link a multitude of documents only to make my visitors wait for them to preload.

    that’s really my one question, unless there’s a way to link to a document that contains all the links, a table of contents or index or sorts…i dunno, my mind is running away with the possibilities.

    (i don’t know if anyone pays attention to these threads anymore, since the article’s a bit old…but there seems to be a tiny issue with spam up there.)

    thanks.

  31. some browsers may preload certain types of links, but unless the link is something like a stylesheet or a favicon etc… it will not delay the display of the page. but to my knowledge, most will not be preloaded at all.

    i beleive in all newer gecko based browsers you can make the browser preload/prefetch by using:

    again, this will not delay the display of the current page. it will be processed after the current page is done displaying.

    heres an nice link

    http://www.mozilla.org/projects/netlib/Link_Prefetching_FAQ.html

  32. Creatively related (but developed independently):

    http://meyerweb.com/eric/tools/s5/

    Eric Meyer’s standards-based slide show (11-12/04), like Christian’s drop-down (5/04) splits one page into many “slides” and automatically generates a dropdown menu by which to navigate from one “slide” to another.

    As you might expect, the *differences* between Eric’s and Christian’s ideas are as interesting as the similarities.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA