Grow your CSS skills. Land your dream job.

Dynamic FAQ Page – A Lesson in Accessibility and Progressive Enhancement

Published by Chris Coyier

The idea is to make a question & answer style page which is embellished in functionality by JavaScript (jQuery). Each question will be shown in a box with a tag hanging from the bottom of the box. When clicked, the tag will slide out and reveal another box with the answer.

View DemoDownload Files

Considerations

This is by no means a difficult thing to accomplish, especially with jQuery. But this example is great for considering the implications of what we are about to do. Our idea takes into account the majority of people who will visit the site, those using a standard browser with JavaScript enabled. But we also need to consider those using a non-standard browser like a screen reader, or those who browse the web with JavaScript disabled.

No JavaScript

In our fully functioning example, the answers are hidden from view until the answer tag is clicked. There are a number of ways to accomplish this, but we should take care to do our hiding with JavaScript, so that when JavaScript is off, the answers are not hidden. Our "answer" pull-tab also becomes a bit irrelevant with JavaScript disabled, so let's also take care to apply that via JavaScript, so it is only present when usable.

Screen Reader

We have already established we will be doing our answer-hiding with JavaScript, so no worries about hiding answers. Our only concern now is that our markup is clean and semantic. In other words, no extra junk that screen reader people don't need to see as well as a clear separation between "question" and "answer".

The Markup

Let's start with the markup then. We'll take advantage of definition lists, which I feel is a perfect match for FAQ content.

<dl class="faq">
	<dt>How much wood would a wood chuck chuck if a wood chuck could chuck wood?</dt>
	<dd class="answer"><div>1,000,000</div></dd>
</dl>

Pretty clean, but there is one bad mark on our semantics rating: the extra div in the answer element. This is not entirely necessary, but it helps the smoothness of the animation we intend to do with jQuery. Long story short, if you use a function like .slideToggle, your animation will be much smoother if that element does not use any padding. We achieve the padding we need through this extra div. More info on this here.

The jQuery JavaScript

First thing first, we load the library and link to our external JavaScript file, to keep things clean. Notice we are loading jQuery from Google here, which is recommended for production. More on how and why.

<script src="http://www.google.com/jsapi"></script>
<script>
  google.load("jquery", "1.2.6");
</script>
<script src="js/faq.js"></script>

Then we write the actual code.

  1. Hide answers.
  2. Append "answer" hang-tab
  3. Apply slideToggle functionality to tab
$(function(){
	$("dd.answer")
		.hide();
	$("dl.faq")
		.append("<dd class='answer-tab-wrap'><a class='answer-tab'>Answer</a></dd>");	
	$(".answer-tab")
		.click(function(){
			$(this)
				.parent()
				.parent()
				.find("dd.answer")
				.slideToggle();
		});
});

The CSS

dl			{ clear: both; margin: 0 0 20px 0; }

dt			{ border: 8px solid #7ac0d0; padding: 10px; background: white; 
			  position: relative; font-style: italic; }
					
dd.answer		{ background: white; 
			  position: relative; width: 90%; margin: 0 auto; }
dd.answer div		{ padding: 10px; border-left: 8px solid #dedede;
                         border-right: 8px solid #dedede; border-bottom: 8px solid #dedede; }
dd.answer-tab-wrap	{ margin: 0 30px 0 0; }

.answer-tab		{ background: url(images/answer-tag.png); display: block; margin: 0 0 20px 0;
                        text-indent: -9999px; width: 105px; height: 50px; float: right; }

Few things of interest here. Because we are floating the answer tab, we'll clear the float on the definition lists themselves to make sure spacing between them works out. We are also using CSS image replacement on the .answer-tab anchor links, for the most efficiency possible (image only needs to load once).

Success

At the top of the page, you can see the results of demo with functionality. Here are the other scenarios:

JavaScript off

Answers visible, styling still makes sense.

Screen Reader

Shown as a webpage with CSS and JavaScript disabled.

Tested & Approved in

Firefox 3, Safari 4, Internet Explorer 6 & 7, Opera 9

Comments

  1. Permalink to comment#

    You’ve got to be kiddin’ me – I was writing an exact same article (“Fancyfying a FAQ page with jQuery). Well, guess you beat me to it.

    Anyway, your article was better anyways, really like this implementation. Keep up the good work Chris!

  2. Permalink to comment#

    From Monty Python , lol !

  3. Permalink to comment#

    Good stuff.

    Unobtrusive JavaScript is something that everyone should keep in mind. In some ways it’s like the CSS layout (vs tables) of client-side functionality. More semantic, better SEO/accessibility, and once you get in the habit, you wouldn’t imagine going back to the old way.

  4. Permalink to comment#

    Great example of using JQuery to enhance a users experience without breaking it when JavaScript is disabled. Great Tutorial!

    -Brenelz

  5. Permalink to comment#

    Love your approach with this tutorial. You have a well defined process that I would love to achieve. I tend to start coding an idea and end up running in circles till I get it right.

  6. Permalink to comment#

    Uhm.. first answer is wrong.
    True answer is ‘A wood chuch would chuck as much wood as a woodchuck could chuck if a woodchuck could chuck wood BUT a woodchuck cannot chuck wood for a woodchuck is a gopher.

  7. Permalink to comment#

    damn, typo.
    -woodchuck-

  8. Permalink to comment#

    Hi Chris,

    Excellent post! I was about to create something similar for a client using jQuery. I believe you just saved me several hours; will use your example as a starting point. Thanks, you rock!

    Brian

  9. I’m a subscriber to your feed here and I currently view my feeds in an external application (I haven’t found a fast enough Firefox add-on) and I had to come here and comment to at least show my appreciation for the time that you put into writing and sharing content such as this.

    Your Dynamic FAQ Page is awesome.
    I’ve already begun to customize it and the possibilities and potential uses for it are many.

    Thanks a lot. Keep the good stuff coming.

  10. What about modern screen readers such as JAWS that do render JavaScript? How do they get the answer when they don’t have a mouse? You need to make the question a link and the answer a bookmark.

    • With JavaScript enabled but CSS disabled, it looks much like the example with both disabled, but just has the text “Answer” below the answer (which is visible). I think that’s pretty graceful degradation.

  11. Permalink to comment#

    Any solutions for having the “Answer” bubble a hand icon instead of an arrow icon? Some of our clients aren’t that Web savvy and would wander around and eventually leave the site because they don’t know to start clicking…

    • Good idea, I added it to the live example. Just need a cursor: pointer; on the .answer-tab in the CSS.

    • I saw this earlier and didn’t say anything… but it leads me on to an interesting request about css caching. If we make changes how would you as a front-end developer with little knowledge about headers/caching/php make it so that your latest CSS is pulled for the user.

      Loads of people make the mistake of using the same file name and the styling looking horrific because of this over sight… so it would be interesting to hear/see your thoughts on it!

    • Permalink to comment#

      I usually use a querystring parameter for that. For example, if my CSS is in styles.css, I would reference it like:

      <link rel=”stylesheet” href=”/path/to/css/styles.css?v=1.0″ />

      Then, if I roll out a change, I alter the v parameter to 1.1 or 2.0. This will force a new download in just about every browser and proxy cache I’ve ever tested.

    • Permalink to comment#

      Thanks! I may just have to incorporate it somewhere!

  12. sadbuttrue
    Permalink to comment#

    Excellent post – especially as it uses progressive enhancement; although, with CSS disabled, the answers are all visible, until you click on ‘Answer’.

    I know it would only take a line of code, but in user experience terms, it would be advantageous for the cursor to change to the hand, especially for when you want the answer to hide.

  13. Al
    Permalink to comment#

    Very nice but takes up a lot of real estate for three questions. Would be nice if an accordian feature was added so that you could separate questions by subject heading.

  14. D. Williams
    Permalink to comment#

    Chris,
    Thanks for all the info you (freely) give out. css-tricks is the first site I check out every time I open a browser (yes, even before checking the email). I have one question. I am working on a small ecommerce site and I want to use jquery to spice up the shopping experience. What I have done is printed out a list of items inside a div with links to click for adding the item to the cart. I want to use jquery to break this list into pages and create a sliding panel sort of interface. The problem I am having is that since I’m using jquery to initially hide the list, the list is briefly flashed on screen before jquery can hide it. I don’t want to hide it with css because if the user has javascript disabled there will be no way to make it visible. Do you (or anyone else) have a tip for me? Thanks again for all your knowledge.

    • Instead of hiding it with CSS via display: none;, you could kick if off the page with some huge negative top and left positioning and position: absolute;. I think that’s generally the technique used there. Then just remove that CSS with the JS.

    • D. Williams
      Permalink to comment#

      I won’t be able to remove that css because javascript will be disabled… am I missing something?

      I notice with your demo the answer block isn’t visible on screen before being hidden with the jquery but there is a brief pause before the “answer” tab is added.

  15. Permalink to comment#

    What can I say? My husband’s name, and I’m not kidding, is Chuck Wood. Oh, and nice tutorial! Thanks!

  16. Permalink to comment#

    This is very cool. I might incorporate this into a project of mine and change it up a little, i’ll let you know how it turns out if i decide to use it.

  17. Permalink to comment#

    This is pretty nice, I’m going to def look into this for my future projects~

    Thanks!

  18. Permalink to comment#

    A wood chuck would chuck as much wood as a wood chuck could.

  19. Brandon
    Permalink to comment#

    This is nice, and pretty. But in terms of usability, the question itself should be click-able to reveal the answer.

    In terms of design (“de-sign”) the “answer” button is not necessary. The standard and expected behavior for FAQs is “user clicks question for which he/she wants the answer”, and this implementation breaks that expectation.

  20. Permalink to comment#

    Nice clean code. I did something like this a year or two ago, of course, I didn’t integrate jQuery. Nice sample.

  21. Permalink to comment#

    Nice tutorial for achieving the desired effect but I would never use this for an FAQ. Why hide the information when people are generally scanning an FAQ looking for information?

    I also think that making a button called “answer” may lead some users to believe they can click and answer questions themselves.

  22. Permalink to comment#

    Great article, Chris. I’m really beginning to understand JQuery, and it has given me ides for my own site. Thanks again. A great 2009 to you.

  23. Permalink to comment#

    Yeah really good tutorial. In case anyone else thinks they’re going mental or maybe it was just me; I originally tried to figure out the jQuery using the code example & it took me ages to figure out where the ‘answer-tab’ came from!? After downloading the demo I realised that this was part of the append with an anchor tag. My mind is at ease now :) Cheers Chris.

  24. Permalink to comment#

    Nice example Chris.

    No-one has mentioned it yet, but if you (someone) worries about the semantics of the extra div – couldn’t you add that with jQuery too?

    • I do agree that adding the extra markup via jquery seams to me like a perfect solution.

      $$('dd.answer')
      .wrapInner('<div></div>') // add div tags
      .hide(); // hide all answers

  25. Permalink to comment#

    I wrote something similar using jQuery a few months ago. I like your styling and simplicity. Have a look at mine if you care to compare…

    FAQ Plugin

    • Al
      Permalink to comment#

      Hi Chris. A demo would be helpful. Thanks.

    • Al
      Permalink to comment#

      Ahh, there it is Neal. I meant an on page demo. I tried doing the same using David Walsh’s Mootools accordian and Chris’ demo but the scripts were conflicting. I’ll try the same with yours.

  26. Neil
    Permalink to comment#

    I really love the look of this, but I have a question.

    I’m quite a jQuery noob but I’m confused as to how the answer-tab class can possibly be clicked if it doesn’t exist in the markup and isn’t appended either. If I’ve totally misunderstood then I’d appreciate someone explaining what’s actually happening here!

    (note – I tried this out using the code above and got a static ‘answer’ text beneath the question that does nothing when clicked. If I change the .answer-tab-wrap to .answer-tab, ie append the answer-tab class so it actually exists in the markup, then things do start to work but it looks more than a little funny. Please let me know where I’m going wrong!)

    I believe I’m also required by convention to point out that I’m a long-time fan of the site, the screencasts and posts are fantastic and I’m always pleased to see a new post go up! Thanks for educating me :)

  27. Permalink to comment#

    No no no.

    A wood chuck could chuck as much wood as he could if you gave him a chuck for every chuck he would.

  28. Permalink to comment#

    Oh and you should give MooTools, or even Prototype, a chance. I started up with Prototype and was hesitant to try MooTools, but am sure glad I did. It is very enjoyable. JQuery just gets a lot of hype because John Resig pushes the name while other are more laid back.

  29. Great tutorial! As I am new to jquery, I would like to learn how to do something similar to what Firefox does. http://www.mozilla.com/en-US/firefox/3.0.5/releasenotes/ While it’s not exactly a FAQ page, I think it would work for one.

  30. S. Thor
    Permalink to comment#

    Hello….

    I tried this with my screen reader (Supernova/HAL 8.0) and it didn’t read out the answer. It read out the question fine and it read “answer” but I was not able to click on the actual answer “button” using the keyboard.

    So..in effect this is not accessible to blind users using screen readers or mobility impaired users who have to rely on keyboard access.

    Thought I should let you know.

    • THANK YOU, for pointing this out. Pretty sorry to screw something like that up in an article that is ABOUT accessibility.

      I believe I have fixed the problem. The problem was that that answer tabs themselves ARE anchor links, but did not contain an href attribute. So, they were unable to be “tabbed” to or other wise individually selected by the keyboard.

      I have fixed this, so tabbing to it and pressing return should activate the link and reveal the answer.

      This would be the case with screen readers that DO work with JavaScript. If it was a JavaScript disabled screen reader, the answers would not be hidden to begin with.

  31. S Thor
    Permalink to comment#

    Hi Chris, thanks for solving that. I tested this again and the screen reader read out the page fine. The only problem with these sort of Javascript/AJAX solutions is that blind users have no way of knowing that the answer actually appears. So lets say that I am reading through the page, I click on “answer” and the answer appears at the top. The reading order of the screen reader is to read from top to bottom so to find the answer I would (logically) TAB down, however the answer appears above…which a blind users has no idea about (as the answer appears “silently”) and usually the blind person would not look for the answer above the “answer” button. It would therefore be better to have the actual answer appear below the “answer” button, that way it is in logical reading order for the screen reader and perfectly accessible to all users :)

    Javascript/AJAX can enhance accessibility for many users but can be tricky for blind users. You just have to take care that the “behavior” of the script makes sense to a screen reader (as the software is pretty stupid) :)

    • I made one more change that will hopefully make things slightly better. I added title attribute to each link that should read out “Reveal Answer”, which should be a better explaining the behavior a bit.

  32. S. Thor
    Permalink to comment#

    Hi again….and sorry to be a pain in the b*

    Same problem. The blind user will not know about the answer below. “Answer” as a name is perfectly clear (and you don’t need to add “Reveal”)……it’s just that user will not know that it is placed above the answer (and will expect it below the “answer” button) since that is the tabbing order.

    Also on LINK TITLES….some screen readers ignore them all together (such as Supernova/HAL) :(

    • Alright more changes =)

      I made some layout changes and code placement changes that I’m hoping might solve this. Now the tab appears along the left and opens the drawer that contains the answer on the right. The answer is actually after the code for the “reveal” link, so I’m hoping that is more obvious.

      Let me know how that goes.

  33. S Thor
    Permalink to comment#

    Ok……

    Firstly I appreciate you trying :)

    I tested this again with my screen reader (and Internet Explorer 6.0). The layout was seriously mixed up (with the “answer buttons” scattered around the page below). In theory though, your thinking should work and when disabling CSS it looks as though it should be perfect, with the answer appearing below.

    When disabling CSS….for some reason the screen reader mixes up the sentence and the “answer” link and does not read out the “answer” link. The screen reader reads out “Answer – Mixed Link” for the “Answer” link and usually when that happens the link itself does not work (not able to click on it). I have no idea why.

    It might be worth “moving” the link so that it appears in the next line when disabling CSS so that is not read out as one sentence? And also take a look at the layout in IE 6.0 :) I haven’t looked at this in IE 7.0. Firefox is ok but it doesn’t work with the screen reader (no worries, it never does).

    Web Accessibility can be a nightmare……!!!

    • Alright the next round is ready =)

      I see what you mean about with CSS off, but that would assume you have CSS off and JavaScript on which would be pretty rare wouldn’t it? Anyway, I think I fixed it so it’s down on the next line.

  34. S. Thor
    Permalink to comment#

    Hurray!!!

    This works perfectly…both for screen readers and mobility impaired users.

    It is good to keep in mind that when you disable CSS, that is the “reading order” of screen readers. The name “screen reader” is in fact not perfectly correct as the software picks up from the html code as well, not just the screen :)

    You are welcome to send me stuff for testing in the future (I am a web accessibility consultant) :)

    Nice work.

This comment thread is closed. If you have important information to share, you can always contact me.

*May or may not contain any actual "CSS" or "Tricks".