Adding Looping to a Content Slider

A few days ago I came across the wonderfully magnificent easySlider jQuery plugin. It makes sliding content vertically or horizontal drop dead simple using totally natural markup and CSS. I immediately worked it into the design of ~/bin (this animation was in my original design on paper) and it worked flawlessly. However, like the plugin’s title implies, it was and is meant to be simple and therefore lacks some features in order to gain ease of use.

I took some time to become more fluent in jQuery, and add some enhancements by modifying the plugin. One of those enhancements was looping, and is the topic of this article. I had no prior experience working with these types of galleries and implementing looping seemed like a fun idea. So here was the result of my tinkering:

How Do You Implement Looping?

Give yourself a minute to get creative. Develop some ideas and strategies on how you would take the following markup for the slides, and make it loop.

<div id="slider">
  <ul>
    <li>content here...</li>
    <li>content here...</li>
    <li>content here...</li>
  </ul>
</div>

I came up with two strategies that use a similar technique to implement the loops. The above loop is an implementation of one of these strategies, but I wanted to compare and contrast my two versions here, and hopefully get some reader feedback on different techniques!

Hidden Moves Strategy

This strategy actually involves physically moving unseen slides around in the DOM. By moving the slides in such a way that there is always the proper “next slide” and looping can continue forever in either direction without errors.

Hidden Moves Strategy

Instantaneous Jumps between Clones Strategy

This strategy actually involves cloning the first slide and adding it to the end of the list. This now makes the first and last slides exactly the same. When we need to handling transitions to and from these slides we can instantly jump between them, and the user is none the wiser.

Instantaneous Jumps Between Clones

Comparing the Pros and Cons of Each

Hidden Moves:

  • Requires no extra space (in theory).

  • Simple to implement moves. 1 line executed as a callback on “next” and before the transition if “prev”

  • Expensive. Requires moving slides on every transition. This is actual DOM manipulation. Sure in nearly all cases this is negligible but it could be a pain for large, complex slides.

  • Wouldn’t work on looping a single slide. Kinda useless though right?

  • Does it work? By taking a slide off of the front won’t everything shift? Yes, this could get tricky, I honestly haven’t tried it.

Instant Jumps:

  • Requires extra space to duplicate the first slide.

  • Simple to clone a slide. 1 line of jQuery.

  • 1 time cost to clone, never again is there a DOM Manipulation cost.

  • Has the ability to loop on a single slide.

  • Tricky code to handle when to jump. Nothing difficult, just messy.

  • Does it work? Yes, Its what I’m using above..

Thats it for my analysis. Do you have any suggestions?

Stack Overflow – Edit Summary Quicklinks

I requested a feature to make the few edit summary suggestions clickable. I, like many developers, don’t normally let my keys leave the keyboard. However, this was one case where I felt making those suggestions of “corrected spelling” and “fixed grammar” should automatically be inserted.

Showing the Usage

Well the suggestion was declined. I can’t blame the team. Nobody upvoted the suggestion. But I felt strongly enough about it, and knew that it was very simple to implement that I whipped up a GreaseMonkey script to do it myself. The script runs like a charm and even adds a few extra suggestions to the original three. It handles formatting and commas all automatically, so don’t worry about a thing, just click. Enjoy!

Script to Add Edit Summary Quicklinks

Script to Prevent Blank Edit Summaries

UPDATE: Fixed to use keng’s URL and added just plain old stackoverflow.com without the beta sub-domain in preparation for a launch. Thanks keng!

DOUBLE UPDATE: Sam put out a “wanted ad” for a Greasemonkey script to prevent blank edit summaries. I whipped that script up and linked to it up above. Thanks Sam! Quick Demo

JavaScript Sort an Array of Objects

I ran into an interesting problem today. I had an array of objects that I wanted sorted on a certain property. My obvious thought didn’t work! (Update: I got a comment below from Peter Michaux who points out a nicer solution, it is included here:)

// Array of Objects
var obj_arr = [ { age: 21, name: "Larry" },
                { age: 34, name: "Curly" },
                { age: 10, name: "Moe" } ];

// This doesn't work!
obj_arr.sort( function(a,b) { return a.name < b.name; });

// This does work! (Peter's update, very fast)
obj_arr1.sort(function(a,b) { return a.name < b.name ? -1 :
                                     a.name > b.name ?  1 : 0; });

That kind of frustrated me. Sorting is one of those things I expect to be available in all languages. I don’t want to have to write a sorting algorithm every time I need to sort. So I looked into things, pulled up a Javascript Quicksort Algorithm and manipulated it to support any compare function.

Now that I have the freedom to truly write a compare function that works for objects! I also changed around certain parts of the code I found online to actually extend the Array class and make the extra functions hidden. Take a look at the sample usage:

// Defaults to (a<=b) sorting.  Great for numbers.
var arr = [1234, 2346, 21234, 3456, 32134, 3456, 1234, 2345, 23, 42523, 1234, 345];

// Object Array
var obj_arr = [ { age: 21, name: "Larry" },
                { age: 34, name: "Curly" },
                { age: 10, name: "Moe" } ];

arr.quick_sort();
// => [23, 345, 1234, 1234, 1234, 2345, 2346, 3456, 3456, 21234, 32134, 42523]

obj_arr.quick_sort(function(a,b) { return a.name < b.name });
// => Curly, Larry, Moe

obj_arr.quick_sort(function(a,b) { return a.age < b.age });
// => Moe (10), Larry (21), Curly (34)

For those who want to see the code be glad, its free. I carried the copyright with it but its rather loose. Grab the JavaScript Source Here! Enjoy:

Array.prototype.swap=function(a, b) {
  var tmp=this[a];
  this[a]=this[b];
  this[b]=tmp;
}

Array.prototype.quick_sort = function(compareFunction) {

  function partition(array, compareFunction, begin, end, pivot) {
    var piv = array[pivot];
    array.swap(pivot, end-1);
    var store = begin;
    for (var ix = begin; ix < end-1; ++ix) {
      if ( compareFunction(array[ix], piv) ) {
        array.swap(store, ix);
        ++store;
      }
    }
    array.swap(end-1, store);
    return store;
  }

  function qsort(array, compareFunction, begin, end) {
    if ( end-1 > begin ) {
      var pivot = begin + Math.floor(Math.random() * (end-begin));
      pivot = partition(array, compareFunction, begin, end, pivot);
      qsort(array, compareFunction, begin, pivot);
      qsort(array, compareFunction, pivot+1, end);
    }
  }

  if ( compareFunction == null ) {
    compareFunction = function(a,b) { return a<=b; };
  }
  qsort(this, compareFunction, 0, this.length);

}

Update

Peter Michaux pointed out something very important. The sort() function can be made to work if it returns numeric output (-1,0,1). His approach is far superior. Here was a benchmark I took:

var obj_arr1 = [];
var obj_arr2 = [];
var filler = [ { age: 21, name: "Larry" },
               { age: 34, name: "Curly" },
               { age: 10, name: "Moe" } ];
for (var i=0; i<5000; i++) {
  rand = Math.floor( Math.random() * 3 );
  obj_arr1.push( filler[rand] );
  obj_arr2.push( filler[rand] );
}

var s = new Date();
obj_arr1.sort(function(a,b) { return a.name < b.name ? -1 : a.name > b.name ? 1 : 0; });
var e = new Date();
console.log(e.getTime()-s.getTime()); // => 75 ms

s = new Date();
obj_arr2.quick_sort(function(a,b) { return a.name < b.name });
e = new Date();
console.log(e.getTime()-s.getTime()); //  => 4444 ms

That shows drastic differences for arrays as large as 5000 elements (with not too random data). 75 ms versus 4444 ms (over 4 seconds). Doing the math: (4444/75) => 59.253 times better! Moral of the story, don’t rush into thinking something doesn’t exist!

So if that’s the way to do it, then I want to make it easier on me. My arrays are generally going to be under 100 in size, and at such a size building a function dynamically instead of writing a custom function works just about as well (although if you were using objects, polymorphism and a compare function would be the best way to go). Here is a simple function I can use to more quickly build compare functions in order to ascend sort an array on multiple properties!

function buildCompareFunction(arr) {
  if (arr && arr.length > 0) {
    return function(a,b) {
      var asub, bsub, prop;
      for (var i=0; i<arr.length; i++) {
        prop = arr[i];
        asub = a[prop];
        bsub = b[prop];
        if ( asub < bsub )
          return -1;
        if ( asub > bsub )
          return 1;
      }
      return 0;
    }
  } else {
    return function(a,b) { return a<=b; };
  }
}

Sample usage would be:

var obj_arr = [
  { name: 'Joe',   age: 20 },
  { name: 'Joe',   age: 10 },
  { name: 'Joe',   age: 30 },
  { name: 'Joe',   age: 40 },
  { name: 'Joe',   age: 20 },
  { name: 'Joe',   age: 15 },
  { name: 'Joe',   age: 35 },
  { name: 'Joe',   age: 25 },
  { name: 'Bill',  age: 5 },
  { name: 'Barry', age: 20 },
  { name: 'Paul',  age: 20 },
  { name: 'Peter', age: 1 },
  { name: 'Smith', age: 25 },
  { name: 'Kary',  age: 30 }
];

obj_arr.sort( buildCompareFunction(['name','age']) );

search