jQuery :random filter


While working on a new JavaScript mockup I needed a way to obtain a random jQuery item. While I could use jQuery(“selector”).get(int) to get a random item, I wanted to have a cleaner way of doing that to keep my code clean.

jQuery ships with great support for extensibility. You can not only extend jQuery with custom functions but filters as well. That is when I thought of the custom “:random” filter, just as you have the standard “:first”, “:last” and “:nth” filters.

Is jQuery capable of returning a random item?

While trying to get the “:random” filter working I’ve stumbled upon a challenge which kept making it all done impossible. As far as my understanding of the jQuery framework goes, it first selects all items based on the selector and then fires the logic defined at the filter to determine whether the particular item should be included in the returned match or not.

As far as I can tell the filter logic is being fired for each match and is supposed to return false if the match should be skipped and true if it should be included in the result. The problematic part here is “fired for each match”: as we are supposed to return a random item we should have one random number and then return only the item of which index is equal to the random number. Because the filter logic is fired for every item you would get a different random number at every run which could result in three scenarios:

  1. it might actually work and you would get exactly one item
  2. the filter would return multiple items, eg. random number 0 for the first item and 2 for third.
  3. the filter would return an empty array: eg. random number 3 for the first item and 0 for the last

The higher the number of matches the smaller the chance for scenario a as the routine for generating random number would be called more often.

Eventually I came with the following solution:

jQuery.jQueryRandom = 0;
jQuery.extend(jQuery.expr[":"],
{
    random: function(a, i, m, r) {
        if (i == 0) {
            jQuery.jQueryRandom = Math.floor(Math.random() * r.length);
        };
        return i == jQuery.jQueryRandom;
    }
});

Each time jQuery calls the “:random” filter the logic is being fired. The i parameter holds the index of the match returned by the selector. If it’s the first element in the match the random number will be generated and stored in the jQueryRandom variable. The random number is being generated based on the total number of matches returned by the selector which can be read from the r parameter. The rest is easy: if the index of the current item is equal to the random number, the current item should be included in the results.

You can use the “:random” filter as follows:

<ul>
    <li>First</li>
    <li>Second</li>
    <li>Third</li>
</ul>
<script type="text/javascript">
    $().ready(function() {
        alert($("li:random").text());
    });
</script>

Enjoy!

Technorati Tags: jQuery, JavaScript

Others found also helpful: