- Content and Markup
- Build the TOC with JavaScript
- Making the Table of Content Collapsible
- Display the Selected Answer
- Summary
Build the TOC with JavaScript
The JavaScript code to build the multilevel TOC is conceptually pretty simple: For every bullet (LI element) in the bullet list (UL element) in the first DIV (identified with id="toc") perform the following:
- Find the corresponding category (find the A element within the LI element and extract the category name from its HREF attribute).
- Find all questions in the category (find a DIV element with id equal to the category name and find all DT elements within it).
- Build a bullet list (UL element) with bullet contents copied from the DT elements.
- Insert the new bullet list after the LI element.
However, because the HTML collections in DOM HTML model (for example, the collection of LI elements in the bullet list) are live, the modifications to the TOC (insertion of a new UL element after the current LI element) will change our data as we work with it. The process thus has to be split into two steps:
- Extract category names from LI and A elements in DIV with id=toc. Store category names and references to LI elements in an object.
- Create bulleted lists and insert them into the table of contents.
The corresponding JavaScript source code is included below. Please note the defensive programming throughout the code—whenever the result is not what’s expected (for example, there is no DIV with id=toc, or the category name in the A element does not match any of the DIV elements), the function exits or (for smaller failures) just continues without modifying the offending element.
function buildToc() { var toc = xGetElementById("toc"); if (!toc) return; var catlist = xGetElementsByTagName("li",toc); var catref = new Object(); /* Step 1: collect category names for LI/A elements and store them in an object */ for (var i = 0; i < catlist.length; i++) { // select a line from the category list and get its // link (A) element var catitem = catlist[i]; var catlink = GetFirstElementByTagName("a",catitem); if (!catlink) break; // extract category name and store it with LI reference var catid = catlink.href.replace(/.*#/,""); catref[catid] = catitem; } /* Step 2: build the new bullet lists in table-of-content */ for (var catid in catref) { // get the category DIV var catdiv = xGetElementById(catid); if (!catdiv) break; // get the DT questions var dtlist = xGetElementsByTagName("dt",catdiv) // create new UL and LI elements var ulElem, liElem; ulElem = document.createElement("UL"); for (var j = 0; j < dtlist.length; j++) { var liElem = document.createElement("LI"); liElem.innerHTML = dtlist[j].innerHTML; ulElem.appendChild(liElem); } // insert the bullet list after the link to the category catref[catid].parentNode.insertBefore(ulElem, catref[catid].nextSibling); } }
The previous JavaScript code generates the second-level entries in the TOC, but they are not yet »live« (the user cannot click on them). We could make them clickable by attaching an onclick event handler to the new LI elements; however, generating links (A elements) for them give our visitors greater flexibility:
- Visually handicapped users with screen readers can recognize new links and navigate them.
- Users that prefer keyboard input to mouse movements will be able to use the TAB key to navigate to the desired link and the SPACE (or ENTER) key to select it.
The insertion of links into the TOC requires two modifications to our JavaScript code:
- As we identify DT elements (questions), we need to assign them unique id values to make them potential anchors.
- The generation of new LI elements must include the creation of an A element.
The modifications to the JavaScript code are included in the listing below and you can download the whole code library from my web site.
var idCount = 0; for (var catid in catref) { // get the category DIV var catdiv = xGetElementById(catid); if (!catdiv) break; // get the DT questions var dtlist = xGetElementsByTagName("dt",catdiv) // create new UL and LI elements var ulElem, liElem; ulElem = document.createElement("UL"); for (var j = 0; j < dtlist.length; j++) { dtlist[j].id = "click_" + (++idCount); var liElem = document.createElement("LI"); var aElem = document.createElement("A"); liElem.appendChild(aElem); aElem.href = "#click_" + idCount; aElem.innerHTML = dtlist[j].innerHTML; ulElem.appendChild(liElem); } // insert the bullet list after the link to the category catref[catid].parentNode.insertBefore(ulElem, catref[catid].nextSibling); }
Last but not least, we need to trigger the code in the previous listing once the web page has been completely loaded. The best way to do it is to tie the buildToc function to the window load event with the following JavaScript code:
xAddEventListener(window,"load",buildToc);
You can test the TOC-building functionality on the sample FAQ page (answer OK to the first question and CANCEL all the subsequent ones).