Creating a semantic FAQ page with definition lists and advanced CSS, Part 2

In Part 1 of this series, we created a semantic FAQ page using definition lists and made it look pretty with CSS. Now it's time to work on some more functionality. Like many FAQ pages, this one is quite long so we need to add some way to navigate through without having to scroll down the entire page.

There are two ways we could do this: duplication the questions on a plain list at the top with targets further down the page, or using JavaScript to make the dd's toggle on and off.

Method 1: A simple navigation list

For simplicity, lets try the simple list first. All we need to do is create a copy of all the questions at the top of the page, remove the answers, and format it as an unordered list (you could argue that this should be an ordered list but I won't go into that right now).

Next, we need to be able to link all of those questions to their answers further down the page. You might be inclined to put in some link targets for each question. That would work, but a tighter solution is available. Did you know that you can link to an id attribute on any element? You can. So what we need to do is put in id attributes for each dt, and link the items in the navigation list to the appropriate ID. Our definition lists now look like this:

<dl>
     <dt id=firstquestion>This is the first question</dt>
     <dd>This is the answer to the first question</dd>

     <dt id=secondquestion>This is the second question</dt>
     <dd>This is the answer to the second question</dd>

     <dt id=thirdquestion>This is the third question</dt>
     <dd>This is the answer to the third question</dd>
</dl>

And our top navigation list looks like this:

<ul>
     <li><a href="#firstquestion">This is the first question</a></li>
     <li><a href="#secondquestion">This is the second question</a></li>
     <li><a href="#thirdquestion">This is the third question</a></li>
</ul>

I also put in a horizontal rule after the navigation list to separate it from the content, and used some CSS to make it look nicer. You can see how that works in the final example, linked below.

Putting in back to top links

I considered using the :after pseudo-element to do this but decided that I couldn't deny this functionality to the majority of visitors. Instead, I put them in as plain text after the answer to each question, like so:

<p class="totop"><a href="#top">back to top</a></p>

Then I added some CSS for the .totop class to align the links to the right and add some margins, and put in an up arrow as an extra visual cue.

We need to have an anchor for the #top link to go to, so I put an id tag in for that too. Guess where? Well, what's the very top level element in the document? The body tag! You could also use the id of another element that is placed at the top of the page, such as a header.

<body id="top">

(We could debate about the methodology for doing back to top links, including the back to top link text but that is beyond the scope of this article.)

File: faq6a-toplist.html

Method 2: Toggling content on and off using JavaScript

Another way to do this would be to keep the answers (dd's) hidden until the user clicks on the question (dt). Clicking on the dt would dynamically show the answer to the question. Using JavaScript is always a little bit risky, but this would provide a much cleaner interface. Let's try it.

First we need a script that will hide and show stuff. We could wrap all of our questions in links that would call a JavaScript function to show the answer. But we don't want to have to go through the whole document and add those in. It could get messy, and there are many opportunities to make mistakes. Instead, what we want to do is call a function when the dt tags are clicked on that would display the dd's.

The first thing we are going to do is create some classes in our CSS to make things turn on and off. We could do this directly in the JavaScript but this way we won't have to repeat it and we can re-use the class whenever we want. Put this into your CSS:

.hide {display: none;}
.show {display: block;}

Now we can start the JavaScript. In a new .js file, we will start by finding our questions and answers and puting them into two variables. To do this we will use the getElementsByTagName property. This will put all of the elements in an array (remember that, we'll need to know that later).

var questions = document.getElementsByTagName('dt');
var answers = document.getElementsByTagName('dd');

Before we forget, we need to put in a link to our new .js file from our HTML page:

<script type="text/JavaScript" src="faq/toggle.js"></script>

Next we will write a function to go through the questions and turn the corresponding answer on when the question is clicked. The function will loop through all the questions (from the questions array variable we already defined), set an onclick handler for each question, and apply a function that will apply the .hide and .show classes to toggle the answer on and off. I have modified this code from BonRouge's Unobtrusive javascript toggle scripts. It looks like this:

function displayToggle(){
     for (i=0; i<questions.length; i++) { // loops through the questions

        questions[i].onclick=function() { // displays the question 
            on clickvar next = this.nextSibling;
            // if it gets to a non-element node, go to the next 
            while(next.nodeType != 1) next=next.nextSibling; 
            onenext.className=((next.className=="hide") ? "show" : "hide");
         }
      }
}

We want to call this function when the page loads, so we need to add in the following code:

window.onload=function() {displayToggle();}

Great, this does almost everything we need it to. The problem we have now is that all the answers are displayed at the start, and we want them to be hidden. We also want to add some links to display all the answers and hide them all again. I'm mentioning this now because we are going to use the same functions to do both.

The functions we need will take all the answers and turn them on and off all at once. To do this, we will use the answers variable we defined earlier, loop through the array, and set some functions to put in our .show and .hide classes.

Here is the code:

// function for the link that turns them all off
function toggleAllOff(){
     for (var i = 0; i < answers.length; i++) {
        answers[i].className = 'hide';
     }
}
        
// function for the link that turns them all on
function toggleAllOn(){
     for (var i = 0; i < answers.length; i++) {
        answers[i].className = 'show';
     }
}

Now we can put two links in our faq page to turn all the definitions on and off:

<a href="#" onclick="JavaScript:toggleAllOn();">Show all answers</a>
<a href="#" onclick="JavaScript:toggleAllOff();">Hide all answers</a>

Check it out! Hide all the answers, show all the answers. To start the page with all answers turned off, we just need to call the toggleAllOff function in the displayToggle funcction that we're calling when the page loads, so we need to add one more line to our displayToggle function:

function displayToggle(){
     // calls the toggle all off function 
     // to turn all the answers off when the page is loaded     
     toggleAllOff(); 

     for (i=0; i<questions.length; i++) {  // loops through the questions
         questions[i].onclick=function() {  // shows the answers onclick
             var next = this.nextSibling;
             // if it gets to a non-element node, go to the next one
             while(next.nodeType != 1) next=next.nextSibling; 
             next.className=((next.className=="hide") ? "show" : "hide");
          }
      }
}

Ta-da! A collapsible FAQ page that degrades gracefully and uses proper web standards!

There are just a few more things I need to do to the CSS to make this page look a little nicer. I'll make my letter smaller, and add some padding around the section headings so they don't run into the definition lists.

Here is the final result and here is the page in actual use. In the latter example I modified the JavaScript to make the list sections collapsible rather than just the individual questions.

Here is a zip file with the HTML, CSS, and JavaScript files used to create this page.

References:

AttachmentSize
faq6a-toplist.html9.28 KB
faq6b-toggle.html9.25 KB
faq_files.zip21.63 KB
Megan McDermott's picture

About the Author

Megan is co-founder and editor of A Padded Cell and administrator at The Webmaster Forums. She has been designing websites since 1997, with expertise in design, information architecture, usability, HTML/CSS, Drupal theming, and more. She is available for short-term or ongoing freelance work in any of those areas. Read her web design blog at MeganMcDermott.com or check out her portfolio.