Skip to Content

Collapsible/Expandable blogrolls on Blogger.com

I’ve just finished co-teaching an online course on blogging for writers through Writers.com with veteran travel/adventure writer Amanda Castleman.  She handles the writing critiques (and most of the lectures) and I provide the tech perspective.  It’s been a good combo and we’ve gotten positive feedback so far.

After going through the final Q&A, it seems there are many similar requests.  The Blogger platform has many advantages, but there are a few glaring holes in their offerings.  Over the next few weeks I’ll outline some patches for those holes.  First up: using jQuery to create a collapsible/expandable blogroll.  Next week I’ll make a simple addition that lets you do this for any widget on your blog.

What we want

  1. We want to give the readers a teaser of what’s available in our blogroll.  We do this by showing the first 10 entries by default and adding a “Show All” link to reveal the rest.  No one is going to click a “Show All” link if they have no idea what is behind the curtain.
  2. While we’re at it, let’s allow readers to roll the list back up, by changing the “Show All” to “Show 10” once the list is revealed.
  3. Using a slide up/slide down effect is oh-so Web 2.0 that we have to do that as well.
  4. Avoid brittle code – if we add or remove blogrolls widgets from our blog, we want this effect to work on all of them.
  5. Finally, let’s use progressive enhancement – only hiding content if a visitor has the ability to unhide it.  Thus, if JavaScript is used to reveal the blogroll, JavaScript will be used to hide it as well.

Before you mess around with our blog’s template, always download a copy of your existing template so you can undo your screw-ups if things go completely awry.  From your Blogger Dashboard, click on Layout, then Edit HTML, then Download Full Template (screenshot) and save it on your computer.  You can upload that file from the same page (screenshot) to undo any changes.

Adding jQuery to a Blogger template

We’re going to use jQuery , an excellent, open-source JavaScript framework to make this happen.  Blogger doesn’t let you upload files to your blog, so we’ll have to rely on someone else to host the jQuery framework.  We could add the entire framework into our blog template, but that would add over 50k to each page on our blog.  Instead we link to jQuery hosted at Google Code.  Now the framework can be cached and our readers can use their bandwidth for downloading porn contributing to Wikipedia.

From Dashboard –> Layout –> Edit HTML, add this line right after the <head> tag near the top of your template.

<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script> 

The strategy

Poking around in the HTML generated by Blogger, it’s easy to pick out the widgets – they are wrapped in <div>s with the class widget. Blogrolls are also given the class BlogList making it very easy to target all the blogrolls in your blog. (Blogger also gives each widget a unique ID, but using that is a more fragile solution. What happens if you had a second blogroll?)  Fortunately, the content of our blogroll is an unordered list.  An example from our class blog looks something like this. I've removed a bunch of extra markup (as well as all the embedded formatting that Blogger insists on dumping all over the place) to simplify the example.

<div id="BlogList1" class="widget BlogList">
  <h2 class="title">Student shout-out</h2>
  <ul id="BlogList1_blogs">
    <li>
      <a target="_blank" href="http://aclearlife.blogspot.com/">A Clear Life (Dave)</a>
      <div class="item-time">
        4 weeks ago
      </div>
    </li>   <li> etc. etc. etc.
  </ul>
</div>

Since we want to leave 10 entries visible, we can’t just hide/show the surrounding <div>.  Instead we’ll adjust the height of the <ul> element and set its overflow to hidden.  Using jQuery’s animate routine, we can make this a smooth transition from one state to the other.

To summarize, for each blogroll we need to

  1. Count the <li> elements.  If there are 10 of less, do nothing.  Otherwise…
  2. Calculate the current height of the <ul> element.
  3. Figure out how tall the <ul> element should be to only show 10 <li> elements.
  4. Add a “Show All” link and set the click hander to toggle between the original height and the 10-elements-only height.
  5. Finally, set the initial state of the blogroll: 10 elements showing and overflow set to hidden.

The code

$(document).ready(function() {
  $('div.BlogList').each(function() {
    if ($(this).find('li').length > 10) {
      var originalHeight = $(this).find('ul').css('height');
      var reducedHeight = (parseInt($(this).find('li:eq(10)').offset().top) 
                           - parseInt($(this).find('ul').offset().top))
                           + 'px'; 

      var link = $('Show All');
      link
        .css('float', 'right')
        .click(function() {
          var me = $(this);
          if ('Show All' == me.text()) {
            // Expand
            me
              .html('Show 10')
              .parent().find('ul').animate({height: originalHeight}, {duration: 500})
            ;
          }
          else {
            // Collapse
            me
              .html('Show All')
              .parent().find('ul').animate({height: reducedHeight}, {duration: 500})
            ;
          }
          return false;
        })
      ;

      $(this)
        .append(link)
        .find('ul')
          .css('height', reducedHeight)
          .css('overflow', 'hidden')
      ;
    }
  });
});

The code in detail

$(document).ready(function() { ... }) allows us to run this code after the document has finished loading.  If you’re unfamiliar with jQuery, take a look at this tutorial on its basics.  Next we grab all blogrolls on the page and run the following code on each of them: $('div.BlogList').each(function() { ... }).

For each blogroll, we find the original height of the <ul> element: var originalHeight = $(this).find('ul').css('height').  To find the height that would show the first 10 elements, we subtract the location of the top of the 11th <li> element ($(this).find('li:eq(10)').offset().top) from the location of the top of the <ul> element ($(this).find('ul').offset().top).  Note that indexes are 0-based so the 11th element is specifed by :eq(10).  Using parseInt is not necessary, but helped me debug these numbers which are otherwise returned as floats.

Next we build the link that will expand and collapse the blogroll: $('<a class="expand-collapse" href="#">Show All</a>').  Before adding that element to the DOM, we setup a handler for the click event which toggles the <ul> element between its originalHeight and its reducedHeight.  Using jQuery’s animate function makes for a simple, slick transition.  You simply supply to the CSS attributes you want to animate – height, in this case – and how long the whole thing should take.

Finally, we add the link just before the blogroll’s closing </div> tag – $(this).append(link) – and set up the initial state – .css('height', reducedHeight).css('overflow', 'hidden').

Save the template file and view the blog.  It should look something like this.

An interesting techie-aside: this example makes a great use of closures, an oddity in some languages that allow variables to remain valid long after they would otherwise be out of scope.  The variables originalHeight and reducedHeight go out of scope as soon as the $(document).ready() function exits.  However, when the click event is called, those variables are used and they retain their correct values.  How does that work? 

When a function is defined within another function (in this case, the click event handler within the $(document).ready() function) local variables of the outer function remain in-scope when the inner function is called.  The inner function is called in the context of the outer function, even if the outer function has exited.

In short, we are saved from having to maintain a global object to store these two values.

Hi Mike

For anyone considering the class, I just finished it and it was marvelous. Really a terrific intro to the subject. I'd be happy to go into detail should anyone be on the fence about taking it. But Mike, I'm not far enough along the learning curve to be reading this blog. You know the old Far Side cartoon about what dogs hear when their masters are talking to them in detail about things? The dogs hear "blah blah, blah, food, blah, blah, blah..." I'm not even up to dog ability yet.

Hi Carrie

Thank you for the kind words about the class -- I'm glad you got a lot out of it!  What I like best about the class is that, first and foremost, it's a writing class.  Amanda does fantanstic line critiques several times through the ten-week session.  My role is mostly to answer questions about the tech-side of things and to introduce students to new technologies that can help them improve their blogging.

Hopefully we'll be running another session starting in early 2010 -- probably starting soon after the New Year.

no teme

I want to quote your post in my blog. It can? And you et an account on Twitter?

blog list

thanks for this code.. really helps..

Post new comment

The content of this field is kept private and will not be shown publicly.