Site Logo

Pixel-in-Gene

Exploring Frontend Development with Design / Graphics / Technology

Complex filtering in isotope.js

Of late, I have been building some Html/Javascript apps and exploring a bunch of javascript libraries, including the usual suspects (jQuery, jQuery UI, jQuery template, underscore, etc). The more interesting ones are visualization libraries like d3, isotope, highcharts. In this post, I will focus on a specific scenario in the isotope.js library.

Isotope.js

Isotope.js is a neat jQuery plugin for doing layout animations and provides a bunch of cool layout modes. When it is applied to a list of items, it lays them out in one of the several layout-modes and animates their positions upon changes in the list. It also provides sorting and filtering hooks which also trigger layout animations when applied. The GIF animation below gives a glimpse of the ‘masonry’ layout mode.

isotope

The way isotope treats filters is by looking for a particular CSS class on each of the items. If the item has that class, it is included in the filtered list. This form of filtering is very declarative and easy to apply.

However if you need more complex filtering, you will have to do some leg work. You will have observed in other toolkits, frameworks that filter is generally considered as a lambda-function that will be invoked for each item in the list. This is a convenient form of providing the filtering logic.

Isotope, however does not think that way and only looks at the class attribute of the item and matches on the supplied filter-class-name. This means, we will have to pre-process the items, apply the complex filtering logic and attach an ad-hoc class to the item. That is the key to complex filtering in isotope: a pre-processing step that attaches an ad-hoc class on matched items, which is then used by isotope.

The code-snipped below shows the pre-processing step before invoking the $.isotope() function. Look for the comments starting with [1], [2], [3].

function createTestData() {
    var people = _.range(0, 100).map(function(p) {
        return {
            name: 'Name-' + p,
            age: Math.round(Math.random() * 50),
            email: 'email-' + p + '@company.com',
            image: 'css/ninja/' + Math.ceil(Math.random() * 6) + '.png',
        };
    });

    $('#person-template')
        .tmpl(people)
        .appendTo('#people-list');
    $('#people-list').isotope({
        layoutMode: 'masonry',
    });
}

$(document).ready(function() {
    createTestData();

    // [1] Filtering function invoked for each item
    function predicate(data) {
        return data.age > 25;
    }

    // [2] Applies the ad-hoc 'matched-item' class to items that match the filter predicate
    $('#invoke').click(function() {
        $('.isotope-item').each(function() {
            var item = $(this).tmplItem().data;

            // [3] Apply the 'matched-item' class
            if (predicate(item)) $(this).addClass('matched-item');
            else $(this).removeClass('matched-item');
        });
        $('#people-list').isotope({
            filter: '.matched-item',
        });
    });

    $('#reset').click(function() {
        $('.isotope-item').each(function() {
            // Clear the class
            $(this).removeClass('matched-item');
        });

        $('#people-list').isotope({
            filter: '*',
        });
    });
});

So filtering in isotope can still be done the traditional way, just that we need a pre-processing step.

Demo

Complex filtering in isotope.js
Pavan Podila
Pavan Podila
July 10th, 2011