Simple jQuery Filtering using Quicksand

11 April, 2011

Updated for 2020!

Please note:  The Quicksand plugin currently only supports jQuery The highest version of jQuery you can go to use quicksand is 1.8.3

Carrying on from the first part of our Simple jQuery Filtering Using jQuery article we're now going to spice up the transitions a little using a jQuery plugin known as Quicksand. The core HTML markup and CSS styling from the first part of the article are going to stay the same so if you have not already done so I suggest downloading the source files before progressing.

Screenshot 1

View post demo
Download post source

You may notice when comparing the source files for the second part of our article that I have moved the CSS and Javascript from inline to their own files (all.css and main.js) to tidy things up a little.  We then need to link to these files from within the "<head>" tags of our HTML document (shown in Step Two).  As well as linking to our external CSS and Javascript documents we also need to make a couple more tweaks to the beginning of our HTML document.

Step One

For the Quicksand plugin to work correctly we need to reference a number of HTML5 tags throughout the markup, to do this we simply need to change the doctype of our html document to the HTML5 doctype - "<!DOCTYPE html>".

<!DOCTYPE html>

Step Two

Before including our external CSS and Javascript files just yet we may as well download the required Quicksand files, we'll then include everything into the top of out HTML document at once.  First off the Quicksand script requires the jQuery Javascript library, we'll include from Google's hosted repository.  Next we need to download the Quicksand script (jquery.quicksand.js), hosted by github.  Finally, and optionally, we require the jQuery Easing Plugin, currently at 1.3.  Create a new folder at the root of our application titled 'js' and save or copy the newly downloaded Javascript files (including our main.js) to here.

HTML

<head>
<meta charset="utf-8">
<link rel="stylesheet" href="css/all.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5/jquery.min.js" type="text/javascript"></script>
<script src="js/jquery.easing.1.3.js" type="text/javascript"></script>
<script src="js/jquery.quicksand.js" type="text/javascript"></script>
<script src="js/main.js" type="text/javascript"></script>
<title>Simple data filtering using jQuery and Quicksand.js</title>
</head>

Step Three

To make sure Quicksand works with our HTML markup we need to make a few alterations from the previous tutorial source code.  Keep the filterOptions ul exactly as we had it before.  Next change the ourHolder div and corresponding div child elements into an unordered list.  We also need to change the ourHolder id to a class.

HTML

<ul class="ourHolder">
  <li class="item" data-id="id-1" data-type="league2">
    <img src="assets/thinktank/images/accrington-stanley.jpg" alt="Accrington Stanley" />
<h3>Accrington Stanley</h3>
</li>
  <li class="item" data-id="id-2" data-type="prem">
    <img src="assets/thinktank/images/arsenal.jpg" alt="Arsenal" />
<h3>Arsenal</h3>
</li>
  <li class="item" data-id="id-3" data-type="league2">
    <img src="assets/thinktank/images/burton-albion.jpg" alt="Burton Albion" />
<h3>Burton Albion</h3>
</li>
  ...</ul>

You will notice from the above script snippet that we have some weird attributes within each li element; this is where our HTML5 comes in to play.  Any attribute that starts with "data-" is treated as a smart storage area by HTML5, replacing the need for using class names and rel attributes for storing metadata to interact with Javascript.  More information about the HTML5 custom data attributes can be found at www.html5doctor.com.  For Quicksand to reference our ourHolder child li elements correctly each one must have a unique "data-id" attribute, then to ensure that the correct items are shown on filter remove our class and assign that to "data-type" (for example, in our first li element we have removed the league2 reference from our class and assigned it to the data-type attribute).

Step Four

After altering our HTML markup to work with Quicksand we need to update our CSS to reflect these changes.  You may notice that we have added some default styling to the html, body tags, this is to ensure that the vertical scrollbar always shows, otherwise (like with any centred content with adjusting heights) the browser window resizes depending on the height out our container and whether a vertical scroll bar is required.  This presents the user with a nasty jumping effect as the browser window resizes and the vertical scroll bar is displayed and hidden.

CSS

/*- GENERIC BODY STYLES -*/
html, body {
  height: 100%;
  margin: 0 0 1px;
  padding: 0;
}
…
/*- FILTER OPTIONS -*/
ul#filterOptions {
  width: 802px;
  height: 52px;
  margin: 30px 0;
  overflow: hidden;
}
ul#filterOptions li {
  height: 52px;
  margin-right: 2px;
  display: inline-block;
  float: left;
}
ul#filterOptions li a {
  height: 50px;
  padding: 0 20px;
  border: 1px solid #999;
  background: #cfcfcf;
  color: #fff;
  font-weight: bold;
  line-height: 50px;
  text-decoration: none;
  display: block;
}
ul#filterOptions li a:hover {
  background: #c9c9c9;
}
ul#filterOptions li.active a {
  background: #999;
}
/*- -*/
/*- OUR DATA HOLDER -*/
ul.ourHolder {
  width: 800px;
  height: 850px;
  overflow: hidden;
}
ul.ourHolder li.item {
  width: 200px;
  height: 200px;
  float: left;
  text-align: center;
  overflow: hidden;
}
ul.ourHolder li.item h3 {
  margin-top: 10px;
  font-size: 16px;
  line-height: 20px;
}
/*- -*/

Step Five

Our fifth step is to set up the Javascript file which will handle the Quicksand plugin.

jQuery

$(document).ready(function() {
  // get the action filter option item on page load
  var $filterType = $('#filterOptions li.active a').attr('class');
  // get and assign the ourHolder element to the
  // $holder varible for use later
  var $holder = $('ul.ourHolder');
  // clone all items within the pre-assigned $holder element
  var $data = $holder.clone();
  // attempt to call Quicksand when a filter option
  // item is clicked
  $('#filterOptions li a').click(function(e) {
    // reset the active class on all the buttons
    $('#filterOptions li').removeClass('active');
    // assign the class of the clicked filter option
    // element to our $filterType variable
    var $filterType = $(this).attr('class');
    $(this).parent().addClass('active');
    if ($filterType == 'all') {
      // assign all li items to the $filteredData var when
      // the 'All' filter option is clicked
      var $filteredData = $data.find('li');
    }
    else {
      // find all li elements that have our required $filterType
      // values for the data-type element
      var $filteredData = $data.find('li[data-type=' + $filterType + ']');
    }
    // call quicksand and assign transition parameters
    $holder.quicksand($filteredData, {
      duration: 800,
      easing: 'easeInOutQuad'
    });
    return false;
  });
});

I've tried to comment each action from within the main.js file to make it as easy to follow as possible.  As you can see this barely resembles our previous Javascript code at all. On document load we start off by assign a "$filterType" variable so Quicksand knows what items it is handling from the offset. Next up we clone the "ourHolder" ul to use when a transaction is required.  A listener is then added which is waiting for a click event to fire on the filter options links, when the event is fired we determine which link has been clicked and in-turn assign a "$filterData" variable to pass to Quicksand.  The "$filterData" variable tells Quicksand what items are required which it is eventually called into action.

Different active menu

In some situations you may want your menu to start on a different tab. There are a few different ways to do this such as a faux click on page load however that would play the animation. A good work around would be simply to run the quicksand on the active class, with duration 0. This will only display the current active class in the HTML.

HTML

...
  <ul id="filterOptions">
    <li><a href="#" class="all">All</a></li>
    <li><a href="#" class="prem">Premier League</a></li>
    <li><a href="#" class="champ">Championship</a></li>
    <li><a href="#" class="league1">League 1</a></li>
    <li class="active"><a href="#" class="league2">League 2</a></li>
  </ul>
...

jQuery


$(document).ready(function() {
  // get the action filter option item on page load
  const filterType = $('#filterOptions li.active a').attr('class');
  // get and assign the ourHolder element to the
  // $holder varible for use later
  const $holder = $('ul.ourHolder');
  // clone all items within the pre-assigned $holder element
  const $data = $holder.clone();
  // assign all li items to the $filteredData var when
  // the 'All' filter option is the active one
  // OR
  // find all li elements that have our required filterType
  // values for the data-type element
  const $filteredData = filterType == 'all' ? $data.find('li') : $data.find('li[data-type=' + filterType + ']');
  $holder.quicksand($filteredData, {
    duration: 0,
  });
  // item is clicked
  $('#filterOptions li a').click(function(e) {
    const $this = $(this);
    // reset the active class on all the buttons
    $('#filterOptions li').removeClass('active');
    // assign the class of the clicked filter option
    // element to our $filterType variable
    const filterType = $this.attr('class');
    $this.parent().addClass('active');
    // assign all li items to the $filteredData variable when
    // the 'All' filter option is the active one
    // OR
    // find all li elements that have our required filterType
    // values for the data-type element
    const $filteredData = filterType == 'all' ? $data.find('li') : $data.find('li[data-type=' + filterType + ']');
    // call quicksand and assign transition parameters
    $holder.quicksand($filteredData, {
        duration: 800,
        easing: 'easeInOutQuad'
    });
    return false;
  });
});

Displaying multiple options

Taking things a step further, why limit things to only one League? Rather than deactivating the other active button, we can start by toggling active on the current button press - this now allows us to have multiple active classes. Now all we have to do is get all the active filterOptions and loop through them using jQuery's each function. The each function loops through every element in an array, allowing us to work with each one individually. This is a very useful function when dealing with arrays, which we will have since there could be more than one active class now. We can now search through data as we did before but this time for every active class. Now since each data search will return an array of its own, we need to merge the arrays into a variable that won't be reset every time the loop runs through. Now all we have to do is run quicksand again and there we have it, displaying multiple options.

jQuery

$(document).ready(function() {
  // get and assign the ourHolder element to the
  // $holder varible for use later
  const $holder = $('ul.ourHolder');
  // clone all items within the pre-assigned $holder element
  const $data = $holder.clone();
  $('#filterOptions li').click(function(e) {
    // assign commonly used variables for faster code
    const $this = $(this);
    // checks if active and adds or removes active class
    if ($this.attr('class') === 'active') {
        $this.removeClass('active');
    } else {
        $this.addClass('active');
    }
    const $filterType = $('#filterOptions li.active');
    // create array that doesnt reset every loop
    let filteredData = [];
    $filterType.children('a').each(function() {
      // Get all corresponding list items
      // that match the data type
      $listEls = $data.find('li[data-type=' + $(this).attr('class') + ']');
      // Store the list items in external array
      filteredData = $.merge(filteredData, $listEls);
    });
    // call quicksand and assign transition parameters
    $holder.quicksand(filteredData, {
      duration: 800,
      easing: 'easeInOutQuad'
    });
    return false;
  });
});

HTML

...
  <ul id="filterOptions">
    <li class="active"><a href="#" class="prem">Premier League</a></li>
    <li class="active"><a href="#" class="champ">Championship</a></li>
    <li class="active"><a href="#" class="league1">League 1</a></li>
    <li class="active"><a href="#" class="league2">League 2</a></li>
  </ul>
...

Now we can set them all to active so the page loads with all of them in and we are are done! Here I have attached the new updated versions if you want to have a play.

View post demo
Download post source

Similarly to above, we can run a copy of what's within the onclick on page load if we want the page to start only on a few categories but as with anything, the best practice is trying it out yourself. Have a go and let me know what funky interfaces you have created!

Written by Stewart

Having been with Evoluted for more than 9 years, Stewart has helped to develop successful websites for countless clients. With vast experience related to web development, he’s comfortable putting his skills to use across a wide variety of projects. His ability was highlighted by the key role he played in a site we built for Bamboo Travel, which won best website at the 2013 Wanderlust Travel Awards.

Up next…
Design Inspiration #1
28 March, 2011

58 Comments

Leave a comment

Replying to: - Cancel