Simplifying FAQ's with jQuery
To kick things off here on the new blog with a little code - I thought I'd share a quick refactoring that I did recently.
You see at my current employer, I've inherited a crap-load of legacy code that was written by a large number of developers of various skill levels. So every day is a bit of an adventure where one day I'm pleasantly surprised by how well some functionality was designed and written - and other days..... well let's just say that some sections of the code make me want to rip my own eyeballs out of my skull.
Today's discussion isn't one of those absolutely horrible examples - but it is one of those items that demonstrates how easy it can be to overcomplicate a solution in ways that makes it no fun to maintain.
In this example - I had been assigned a story to update the frequently asked questions (FAQ) page for our application with seven new entries. I opened up the view template and found that the the FAQ entries were built using an ordered list that looked like this:
<ol class="faq_list">
<li onclick="$('faq_1').toggle();">
<p class="question">
Is this implementation too verbose?
</p>
<p class="answer" id="faq_1">
Yes, god yes.
</p>
</li>
<li onclick="$('faq_2').toggle();">
<p class="question">
Is this something that you would want to have to mess with?
</p>
<p class="answer" id="faq_2">
No! This weak crap gets old really fast.
</p>
</li>
</ol>
So you can see that each question and answer were individually labeled with unique ID's designating their position in the list and that each list element had an onclick even embedded directly into the source that would toggle the display of the associated answer for each question.
Now to hide all those answers on page load there was a block of JavaScript at the bottom of the page that looked like this:
$('faq_1').hide();
$('faq_2').hide();
$('faq_3').hide();
Now this solution might have made sense when the FAQ page only had 2 or 3 entries, but currently it had near 20 entries and I was about to add seven more. To make matters worse - the new ones were going to be inserted at various places within the existing list and not just seven new entries at the bottom.
So I could have done the easiest thing possible and simply worked those new answers into this existing template but that's just not the right thing to do. This solution was just too complex and I would have felt dirty if I had left it like this.
In these situation - the first thing I'm going to do is load up jQuery into the view, as jQuery has surprisingly become my JS framework of choice.
<%= javascript_include_tag 'jquery/jquery' %>
Unfortunately though, the initial group of developers for this application preferred other JS frameworks (technically they preferred multiple JS frameworks -- which is a rant for another day), and I'm currently in the process of slowly replacing those other frameworks with jQuery. So the next thing I need to do is tell jQuery to run in compatibility mode using the noConflict method:
jQuery.noConflict();
Now that we have the template setup to use some better tools - I can go ahead and clean up the HTML implementation of those questions/answers by removing all those unnecessary onclick events and extraneous identifiers. So afterwards it should look like this:
<ol class="faq_list">
<li>
<p class="question">Is this getting any better?</p>
<p class="answer">Yes, simpler is always better</p>
</li>
<li>
<p class="question">Will this still be painful to edit?</p>
<p class="answer">No. Now this is just some simple HTML</p>
</li>
</ol>
All that's left now is to add a few simple jQuery calls to handle our toggling the display of our questions and answers. To do that we build our code inside of the document ready event (which checks the document and waits until it's ready to be manipulated before executing it's code.)
jQuery(document).ready(function() {
});
Within this ready event we'll first hide all elements in our document that have a class of "answer".
jQuery(".answer").hide();
Finally, we'll add a click handler onto every element that has a class of "question". Within this click handler - we'll grab a reference to the very next element after the "question" class (which will be the related answer) using the next function and we'll call the toggle function on it.
jQuery(".question").click(function () {
jQuery(this).next().toggle();
});
In the end - our final solution that will now make adding new FAQ entries as easy as adding a couple HTML elements with a 'question' and 'answer' class will look like this when it's all put together.
jQuery.noConflict();
jQuery(document).ready(function() {
jQuery(".answer").hide();
jQuery(".question").click(function () {
jQuery(this).next().toggle();
});
});
Much simpler, and it would be a trivial matter to now extend this solution to use fancier animation or to do add a link that would expand all answers on the page.



Monday, March 16, 2009 at 3:13AM
Reader Comments (2)
useful post
Mbt zapatos Sin embargo, todas las flores, mucho tiempo, y claro del amor, las parejas, el amor era más le gusta, entonces, no sé, este amor, como tú, de pie de la ventana, mucho tiempo sin volver a todo color , el mundo está infectado con un halo blanco abierta tus lágrimas, no te vayas, no separado de la ventana abierta en los pensamientos en el mar no puede---Mbt zapatos