11 January 2009

Update: Creating a news carousel with jQuery, now with time based switching

This is just a simple and quick update on the Creating a news carousel with jQuery post.

After reading this comment, and going through the code, I decided to implement the time-based switching functionality and also clean up the code a little bit (check it out here).

The additions made (along with some code cleaning) were:

  • Append a simple div that will shrink while the picture is shown and reinitialized when the picture is switched.
  • Add a setInterval call that will do the picture switching (and the new div's animation).

Update 01/12: I added some fixes to the code

  • Use the image's load event to calculate each individual width. When all images are loaded, then the carousel is initiated.
  • The animate_timer function now stops all animations on the timer div before reinitializing the animation
Update 01/27: Even more fixes :)
  • Work with cases were images are already in cache and load event is fired before we attach to it.
  • Fixed the way the news animation was calculated.
  • Added 2 more news to help test it better.

So here's the new javascript that will do this:

$(function() {
    var carousel   = $('#news_carousel');
    var news       = carousel.find('ul.news');
    var controls   = null; // Will hold the ul with the controls
    var timer      = null; // Will hold the timer div
    var wait       = 5000; // Milliseconds to wait for auto-switching
    var widths     = [];   // Will hold the widths of each image
    var items_size = news.find('li').length;
    var initialized = false;

    if (!items_size) { return; }

    // Controls html to append
    var controls_str = '<ul class="controls">';
    for ( var i = 1; i <= items_size; i++) {
       controls_str += '<li><a href="#">' + i + '</a></li>';
    }
    controls_str += '</ul>';

    // Cache the controls list
    controls = carousel.append(controls_str).find('ul.controls');

    // Make the first button in controls active
    controls.find('li:first a').addClass('active');

    // Hook to the controls' click events
    controls.find('li a').click(function(event) {
      move_news( $(this) );
      return false;
    });

    // Append the timer and cache it
    timer = carousel.append('<div class="timer"></div>').find('div.timer');

    // Store each item's width and calculate the total width
    news.find('li img')
        .each(function(i, e) {
            widths[i] = $(e).width();
            if ( all_images_loaded() ) { init_carousel(); }
        })
        .load(function(e) {
            var i = news.find('li img').index(this);
            widths[i] = $(this).width();
            if ( all_images_loaded() ) { init_carousel(); }
        });


    function all_images_loaded() {
      if (items_size == widths.length) && (jQuery.inArray(0, widths)  1 ) {
        return false;
      }

      var current_active = controls.find('li a.active');

      if (new_active == 'next') {
        var next = current_active.parent().next().find('a');

        if ( !next.length ) { next = controls.find('li:first a'); }

        new_active = next;
      }

      var current_index = parseInt(current_active.text(), 10) - 1;
      var new_index     = parseInt(new_active.text(), 10) - 1;
      var move_to       = new_index - current_index;


      if (!move_to) { return false; }

      var direction = (move_to > 0)? '-=': '+=';

      var move   = 0;
      var bottom = Math.min(current_index, new_index);
      var top    = Math.max(current_index, new_index);

      while (bottom < top) {
        move += widths[bottom];
        bottom++;
      }

      news.animate({marginLeft: direction + move }, 500);
      new_active.addClass('active');
      current_active.removeClass('active');
    }

    function animate_timer() {
      timer.stop().css({width: '100px'}).animate({width: '1px'}, wait);
    }

    // Initializer, called when all images are loaded
    function init_carousel() {
      if (initialized) { return false; }

      // Set the news ul total width
      var width = 0;
      for( var i = 0; i < widths.length; i++) { width += widths[i]; }
      news.width(width);

      // Make the news change every X seconds
      setInterval(function() { move_news('next'); animate_timer(); }, wait);
      animate_timer();

      initialized = true;
    }
});

11 January 2009

jQuery Object Cache Update

After discussing with Andrew Luetgers some improvements that could be done to the Object Cache plugin, I updated the plugin to a new version.

The new improvement is that now you can store a selection automatically:

// Will store in cache $("#sidebarNav") with #sidebarNav key
$$("#sidebarNav");

This will let you avoid doing $("#menu").cache("menu"), which is a bit redundant. Now by doing $$("#menu") you will be able to store it in the cache and retrieve it automatically. There is also a reload option that you can pass to that call in order to, well, reload the the cached object with that selection:

// Will reload #sidebarNav with $("#sidebarNav")
$$("#sidebarNav", true);

You can grab the new version of the Object Cache plugin here.