I was recently looking for a decent way of shuffling a set of elements. I found a jQuery plugin which claimed to do exactly that but unfortunately it only works if all elements are direct descendants of a single parent, like all list items under an unordered list.

I needed something that would work with any elements, regardless of their position within the DOM. In the end I didn’t find a solution so I attempted it myself, and here’s the end result:

$.fn.shuffle

(function($){
 
    $.fn.shuffle = function() {
 
        var allElems = this.get(),
            getRandom = function(max) {
                return Math.floor(Math.random() * max);
            },
            shuffled = $.map(allElems, function(){
                var random = getRandom(allElems.length),
                    randEl = $(allElems[random]).clone(true)[0];
                allElems.splice(random, 1);
                return randEl;
           });
 
        this.each(function(i){
            $(this).replaceWith($(shuffled[i]));
        });
 
        return $(shuffled);
 
    };
 
})(jQuery);

Usage

// Shuffle all list items within a list:
$('ul#list li').shuffle();
 
// Shuffle all DIVs within the document:
$('div').shuffle();
 
// Shuffle all <a>s and <em>s:
$('a,em').shuffle();

This plugin can be used on any set of elements, obviously performance needs to be taken into account when shuffling many elements.

If you are worried about performance then you might prefer this pure JavaScript method (~10% faster):

// WARNING: I haven't yet tested this
// - It's likely that it will strip events upon shuffling
function shuffle(elems) {
 
    allElems = (function(){
	var ret = [], l = elems.length;
	while (l--) { ret[ret.length] = elems[l]; }
	return ret;
    })();
 
    var shuffled = (function(){
        var l = allElems.length, ret = [];
        while (l--) {
            var random = Math.floor(Math.random() * allElems.length),
                randEl = allElems[random].cloneNode(true);
            allElems.splice(random, 1);
            ret[ret.length] = randEl;
        }
        return ret; 
    })(), l = elems.length;
 
    while (l--) {
        elems[l].parentNode.insertBefore(shuffled[l], elems[l].nextSibling);
        elems[l].parentNode.removeChild(elems[l]);
    }
 
}
 
 
// Usage:
shuffle( document.getElementsByTagName('li') );

Thanks for reading! Please share your thoughts with me on Twitter. Have a great day!