Spice up your FAQ page with jQuery

FAQ pages are fairly common place on websites these days, and quite rightly so, and there are some show/hide and accordion effects that are commonly used to display them. The thing is, I see many sites that use jQuery plug-ins to achieve this, even though the necessary code is relatively lightweight and simple. In short, a plug-in is overkill.

There are a couple of things we want to keep in mind here. The first is that we want this code to be flexible and secondly we want this code to degrade gracefully so that the content is accessible, even without JavaScript enabled.

Start with the content

Starting from the content out is very important. This is the easiest way to mark-up content semantically, starting with the content wrapped in paragraph and heading tags etc. and then adding containers as and when it makes sense to.

Use CSS to style the content

Next up we want to style this page, according to the design of the site we are developing. Obviously here I have kept things fairly simple as I want this example to be as clear as possible. The thing to remember here is that you are styling this page as if there was no JavaScript functionality—that comes later.

Add functionality with JavaScript

Next up we add the JavaScript functionality. Firstly I am going to show you the complete code, then walk you through what each part of it does. Another thing to note here is I have added a class with JavaScript to the questions so they can be styled to behave like links. Of course you can use this to do anything you want but it is a good idea to make it clear that the questions are clickable.

Here is the finished code, in all it’s glory:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$(document).ready(function() {

  // Add a class to nav item for styling purposes
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
    $(this).toggleClass('open').siblings('li').removeClass('open');
    $(faqSection).children('p:first').show().siblings('p').hide();
  });

  // Hide all anwsers except the first
  $('[role="main"] article p:not(:first)').hide();

  // Add onclick function to question to open answer and close all others
  $('[role="main"] article h3').addClass('trigger').click(function() {
    $(this).next('p').slideToggle('fast').siblings('p:visible').slideUp('fast');
  });

});

Step by step

It should go without saying that you should link to the jQuery library above this code in your document. I like to do this just before the closing body tag, and then link to any plug-ins or custom scripts below. This helps your page load quicker, which is never a bad thing.

When the page has loaded

All of our code will be wrapped in the following statement:

1
2
3
4
5
$(document).ready(function() {

  // Code will go here

});

This code basically means our functions will be executed once the page has loaded, and not before.

Showing where we are

It’s a good idea to highlight the selected navigation link to give the user an indication of which section they have open. To do this I have used the addClass() method to add a class of open to the current link:

1
2
3
4
5
6
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

});

Hide everything, except the first group

Next up, we want to hide each group of questions, except for the first one, which we want to be open by default on page load:

1
2
3
4
5
6
7
8
9
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

});

Build the show/hide function

Right, now we need to start building the show/hide functionality. We do this by creating an onclick event on the navigaton list item:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
  });

});

We add e.preventDefault() here to prevent the ID of the section showing in the URL. This is purely a preferential thing and some may argue that it is more accessible to have this in the URL. The choice is yours.

Then we create a variable for the group of questions, using the attribute of each link:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
  });

});

Then we use the fadeIn() method to display each group of questions when they are selected and the hide() method to hide all of the other groups:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
  });

});

After this we want to toggle the ‘open’ class on the list item and also make sure it is removed from any siblings that are not currently active:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$(document).ready(function() {

  // Adds a class to the 'active' nav item
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
    $(this).toggleClass('open').siblings('li').removeClass('open');
  });

});

Now we have the full functionality to show and hide the groups of questions, depending on which link is clicked, but we’re not finished yet. Now we want to add an accordion effect to the questions within each group.

Firstly we want to be sure that when each section in revealed, only the first answer is showing and all others are hidden. To do this we need to add one more line to our function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$(document).ready(function() {

  // Add a class to nav item for styling purposes
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
    $(this).toggleClass('open').siblings('li').removeClass('open');
    $(faqSection).children('p:first').show().siblings('p').hide();
  });

});

Now we want to do the same outside of the above function:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$(document).ready(function() {

  // Add a class to nav item for styling purposes
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
    $(this).toggleClass('open').siblings('li').removeClass('open');
    $(faqSection).children('p:first').show().siblings('p').hide();
  });

  // Hide all anwsers except the first
  $('[role="main"] article p:not(:first)').hide();

});

And we’re almost done. We just want to add another function to toggle the display of the answers within each section, and while we’re at it, add a class so that we can style the question as a link, or any way you wish to style it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
$(document).ready(function() {

  // Add a class to nav item for styling purposes
  $('nav li:first').addClass('open');

  // Hide all question groups except the first
  $('[role="main"] article:not(:first)').hide();

  // Add an onclick function to the list item to open section and close all others
  $('nav li').click(function(e) {
    e.preventDefault();
    var faqSection = $(this).children('a').attr('href');
    $(faqSection).fadeIn('slow').siblings('article').hide();
    $(this).toggleClass('open').siblings('li').removeClass('open');
    $(faqSection).children('p:first').show().siblings('p').hide();
  });

  // Hide all anwsers except the first
  $('[role="main"] article p:not(:first)').hide();

  // Add onclick function to question to open answer and close all others
  $('[role="main"] article h3').addClass('trigger').click(function() {
    $(this).next('p').slideToggle('fast').siblings('p:visible').slideUp('fast');
  });

});

Conclusion

And there we have it. Some common JavaScript functionality for FAQ pages and the like that doesn’t require a plug-in and also degrades gracefully.

Of course if your answers were to contain more than one paragraph then you would need to substitute the p tag for a container of some kind, maybe a div, maybe a section or maybe even an article, but that is up to you.

If you have anything to add to this code or think that this could be achieved with even less code then I would really love to hear about it in the comments of this post. I’m open to all criticism, good and bad, just as long as it has a point, so don’t be shy.