To give a simple example of how things are not user friendly is if you go to ebay.com or ebay.in. Its frankly appalling! For every click and every selection, the entire page refreshes. Though its good for showing Ad impressions (which I hate) when browsing an e-commerce site, the usability is lost.
On the other hand, have a look at snapdeal.com or groupon.com and it tells a different story. They fire Apis to their server to get json data and render that via Javascript. Usability is excellent. (no wonder they are doing so well!)
We did client-side search filtering in a couple of portals we built and realized that we should generalize this. That got us started and we ended up with filter.js This is a Javascript module that we have tried and tested on various portals.
Some key features are:
- Category and sub-category based filtering
- Slider based filtering
- Trigger on any HTML element.
- Date filtering (in process)
To use filter.js is simple. Add this to your section:
1
|
< script src = "https://raw.github.com/jiren/filter.js/master/filter.js" type = "text/javascript" ></ script > |
There are dependencies on how to render HTML components. Currently, its our own custom code but we have plans to upgrade this to integrate with mustache or handlebars. For now, we live with it
Defining the search criteria and the JSON objects
You can generate JSON data in any way you wish. Remember that the top-level entity will be used for rendering the view – in this case ‘person’.
1
2
3
4
5
6
7
|
var people = [{person: {name: 'Jiren' , age:26, country: 'India' , country_id: 1, states : [{ state : 'MH' , state_id : 3 }, {state : 'HN' , state_id : 4}] } }, {person: {name: 'Joe' , age:25, country: 'USA' , country_id: 2, states : [{ state : 'MH' , state_id : 3 }, {state : 'HN' , state_id : 4}] } } ] |
Defining the view function
We have some custom code for rendering HTML components. This assumes that you have the json objects in place. Here is an exmaple:
1
2
3
4
5
6
|
var view = function (person){ name = this .span({ 'class' : 'name' }, person.name); age = this .span({ 'class' : 'age' }, person.age); country = this .div({ 'class' : 'country' }, person.country); return this .link( '/demo/' + person.id ,{ 'title' : person.name}, [name,age,country]); }; |
A more detailed example is available in the examples given on github. To define the filter criteria, all you need to do is pass a combination of the HTML components, the events and the JSON object attributes. For example:
1
2
3
4
5
6
7
8
9
10
11
|
var settings = { filter_criteria: { country: [ '#country_list input:checkbox .EVENT.click .SELECT.:checked' , 'country_id' ], age: [ '#age_list input:checkbox .EVENT.click .SELECT.:checked .TYPE.range' , 'age' ], price: [ '#price_filter .EVENT.change .SELECT.:input .TYPE.range' , 'timeleft' ] states: [ '#state_list input:checkbox .EVENT.click .SELECT.:checked' , 'states.ARRAY.state_id' ], } }; /* Trigger the filtering */ var fJS = new filterJS(people, "#people_list" , view, settings); |
Here is how this works:
Category based filtering
1
|
[ '#country_list input:checkbox .EVENT.click .SELECT.:checked' , 'country_id' ] |
All the checkboxes under the
are used as the selectors. When the checkbox click event is triggered for any checkbox, all the checked check-boxes are used as the filter criteria and the JSON objects which match the ‘country_id‘ will be rendered using the view function. Rendering is done only once and then the display is toggled as needed.
Range based filtering via checkbox
1
|
age: [ '#age_list input:checkbox .EVENT.click .SELECT.:checked .TYPE.range' , 'age' ] |
Its exactly the same as shown earlier except for the additional .TYPE.range attribute. This tells the filter.js that it needs to pick up the range from the HTML element for filtering. This is typically added like this:
1
2
3
4
5
6
7
|
< div id = 'age_list' > ... < input type = 'checkbox' value = "below-30" > < input type = 'checkbox' value = "30-50" > < input type = 'checkbox' value = "50-above" > ... </ div > |
So, now the filtering will trigger on the the range of age – this can be customized as you wish. Note the boundary limiters ‘below-30′ and ’50-above’.
Range based filtering via slider
1
|
price: [ '#price_filter .EVENT.change .SELECT.:input .TYPE.range' , 'timeleft' ] |
Here the slider id change event triggers the filter and the value of the slider is used to filter the JSON data. Here is the sample HTML code. Ensure that you have jquery-ui.js included in the header.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< script type = "text/javascript" > $( "#price_slider" ).slider({ range:true, min: 0, max: 1000, values:[0, 500], step: 5, slide: function( event, ui ) { $( "#price_range_label" ).html('$' + ui.values[ 0 ] + ' - $' + ui.values[ 1 ] ); $('#price_filter').val(ui.values[0] + '-' + ui.values[1]); $('#price_filter').trigger('change'); } }); </ script > < span id = "price_range_label" >$0-$100</ span > < div id = "price_slider" ></ div > < input type = "hidden" id = "price_filter" /> |
Demo Time
Have a look at http://www.goodinkind.com/services and http://www.goodinkind.com/nonprofits to see filter.js in action in 2 different scenarios!
Alternatively, you can also clone the github repository and see the demos/filterjs.html
We look forward to some feedback from you and hopefully some contributions!
Filter away!
Update1
Thanks for the overwhelming response! I have added mustache templating for rendering the HTML view snippets.
Define mustache.js template in html page.
1
2
3
4
5
6
7
|
< script id = "person_template" type = "text/mustache" > < a href = "/demo/{{id}}" title = "{{name}}" > < span class = "name" >{{name}}</ span > < span class = "age" >{{age}}</ span > < div class = "country" >{{country}}</ div > </ a > </ script > |
View function:
1
2
3
4
|
var mustache_template = $( "#person_template" ).html(); //Find template data. var mustacheView = function (person){ return Mustache.to_html(mustache_template, person); }; |