Search
Books I've Written

Practical Rails Projects

Takes a focused approach to guiding you through the creation of multiple real-world examples that are designed to get your hands dirty with the core features of Rails, while providing you with the valuable experience of creating real Rails applications.

 

Foundation Rails 2

Foundation Rails 2 takes you through your first steps in Rails, explaining in plain English how to start building dynamic web applications. And there's never been a better time to jump in to the Rails world, as the release of Rails 2 is a major evolutionary leap forward from previous versions.

« Blog Update | Main | Being Premature »
Monday
16Mar2009

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.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments (1)

useful post

November 28, 2009 | Unregistered CommenterRopa

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>