List.js is a lightweight javascript plugin that adds search, sort, and filter features to HTML elements. The plugin works on <table> elements as well as block-level HTML.
In this tutorial, I will use the plugin to create a list of items (using the <a>
tag), count them (with jQuery), then make the list sortable and add some dynamic filters.
Lists provide a convienient way to consume a large amount of (text-heavy) content. They are great for scanning and finding important information, as the user flow runs in a single vertical direction, and they generally take up less space than a grid so they work on smaller displays.
Also, just about every app contains some form of list, so the user behaviour is established and consistent.
“The time it takes to make a decision increases with the number and complexity of choices.”
William Edmund Hick and Ray Hyman (Hick’s Law)
But, according to Hick’s Law, having many options increases the time taken to make decisions.
By using filter logic on a list users can reduce the amount of items they see. Sorting empowers users to order items in a way that meets their interests and needs. When implemented properly, filters and sorting will increase the likeliness of the user finding what they are looking for in a list.
For further reading, check out the these UX best practices for implementing filter and sort.
Before you start, download and reference the following scripts:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/list.js/1.5.0/list.min.js" type="text/javascript"></script>
Then, we need to build our HTML template for the list and add some simple styling.
#albums
..list
element must be used to contain the items..list__item
in the list, each item will have an .album
title, .artist
and release .date
..header
contains an empty span.count
element, we will populate this later with the number of items in the list.<div class="container" id="albums">
<div class="header">British Bangers! <span class="count"></span></div>
<div class="list">
<a class="list__item" href="javascript:void(0)">
<div class="album">Definately Maybe</div>
<div class="meta">
<div class="artist">Oasis</div>
<div class="date">2002</div>
</div>
</a>
</div>
</div>
Here we set a few basic variables, such as color and font. As well as the global styles for our list container.
@import url('https://fonts.googleapis.com/css?family=Cabin:400,600');
$base_color: rgba(0,0,0,1);
$accent_color: dimgrey;
$primary_color: #E27;
$bg_color: #ede8e8;
html,
body {
min-height: 100%;
}
body {
margin: 0;
padding: 0;
min-width: 200px;
background: $bg_color;
color: $base_color;
font-family: 'Cabin';
font-family: 16px;
line-height: 1.6;
}
a {
text-decoration: none;
}
.container {
max-width: 500px;
padding: 80px 1.5rem;
margin: 0 auto;
}
I have used flexbox to quickly position the .count
at the top right of the header.
.header {
display: flex;
align-items: center;
font-size: 2rem;
font-weight: 600;
border-bottom: 1px solid lighten($base_color, 84%);
margin-bottom: 2rem;
padding-bottom: 1rem;
}
.count {
padding: .3rem;
border-radius: 1rem;
font-size: 1rem;
margin-left: .5rem;
margin-top: -2rem;
min-width: 1.5rem;
line-height: 1.2;
text-align: center;
color: white;
background: $primary_color;
}
Of course you can style your list however you like, for simplicity, I have applied a simple ‘card’ effect to each item, grouping the artist and date below the album title. Each item is also a link so I have added a simple shadow hover effect.
.list__item {
display: block;
padding: 1rem;
margin: 0 0 1rem 0;
background: white;
border-radius: 1rem;
color: inherit;
transition: all .2s ease;
&:hover {
color: $primary_color !important;
box-shadow: 0 1px 10px 0 transparentize($base_color, .9);
}
}
.album {
font-size: 1.25rem;
color: inherit !important;
font-weight: 600;
margin-bottom: .25rem;
}
.meta {
display: flex;
flex-wrap: wrap;
font-size: .9rem;
color: $accent_color;
> div {
padding-right: .5rem;
}
> div:not(:last-child):after {
content: '·';
padding-left: .5rem;
display: inline-block;
}
}
Now that the basic list is ready we need to create, populate and run the list using javascript.
valueNames
in the list options.var options = {
valueNames: [ 'album', 'artist', 'date' ]
};
values
.var values = [
{
album: 'Sense',
artist: 'The Lightning Seeds',
date: 1992
},
{
album: "His 'N' Hers",
artist: 'Pulp',
date: 1994
},
...
];
var albums = new List('albums', options, values);
albums.sort("album", {
order: "asc"
})
Check out the List.js documentation for more details on how to initialize the list.
List.js makes it super easy to sort our list, we have already set our list to sort by album name on load, but we can also create some controls that will dynamically sort the list when clicked by the user.
#albums
in this example)..sort
class and a data-sort
attribute that matches the valueNames
set previously.<div class="controls">
<div>Sort by:</div>
<a class="sort" href="#" data-sort="album">Album</a>
<a class="sort" href="#" data-sort="artist">Artist</a>
<a class="sort" href="#" data-sort="date">Date </a>
</div>
.asc
and .desc
classes respectively, so we can add some styles to indicate when the sort button has been triggered..controls {
display: flex;
align-items: center;
margin-bottom: 1rem;
color: $base_color;
> div {
font-weight: 600;
}
> * {
margin-right: 1rem;
color: inherit;
transition: all .2s ease;
}
a:hover {
color: $primary_color;
}
.sort {
display: flex;
align-items: flex-end;
&:after {
display: block;
opacity: 0;
content: '';
margin-left: .25rem;
font-family: 'Material Icons';
font-weight: 400;
font-style: normal;
font-size: 1.5rem;
display: inline-block;
line-height: 1;
text-transform: none;
letter-spacing: normal;
word-wrap: normal;
white-space: nowrap;
direction: ltr;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale;
font-feature-settings: 'liga';
}
&.asc, &.desc {
color: $primary_color;
}
&.asc:after {
opacity: 1;
content: 'arrow_drop_up';
}
&.desc:after {
opacity: 1;
content: 'arrow_drop_down';
}
}
}
Unlike sort, filters require a little more effort to initiate effectively. The logic I wanto to create here is as follows:
To implement this, first we need to build out the HTML for our group of filters and style it.
.tags
and .tag
classes..filter
class and data-filter
attribute are used to control the logic.<div class="tags">
<div>Filter by:</div>
<div class="tag filter" data-filter="1993">'93 </div>
<div class="tag filter" data-filter="1994">'94 </div>
<div class="tag filter" data-filter="1995">'95</div>
<div class="tag filter" data-filter="1996">'96 </div>
</div>
.tags {
display: flex;
align-items: baseline;
flex-wrap: wrap;
margin-bottom: .75rem;
div {
font-weight: 600;
margin-right: .5rem;
}
.tag {
display: flex;
cursor: pointer;
margin-bottom: .5rem;
margin-right: .5rem;
color: white;
background: lighten($base_color, 60%);
padding: .2rem .6rem .2rem .6rem;
font-weight: 600;
font-size: .9rem;
transition: all .2s ease;
border-radius: 1rem;
overflow: hidden;
&:after {
content: 'x';
cursor: pointer;
opacity: 0;
visibility: hidden;
max-width: 0;
transition: all .2s ease;
}
&.active:after {
opacity: 1;
visibility: visible;
max-width: 2rem;
margin-left: .4rem;
}
&:hover,
&.active {
background: $primary_color;
}
}
}
The filter logic is controled by some simple jQuery. I decided that I only wanted to let users filter by date.
data-filter
value, checking to see if any items in the list return a date
value match..active
class is applied to the filter button.$('.filter').on('click',function(){
var $q = $(this).attr('data-filter');
if($(this).hasClass('active')){
albums.filter();
$('.filter').removeClass('active');
} else {
albums.filter(function(item) {
return (item.values().date == $q);
});
$('.filter').removeClass('active');
$(this).addClass('active');
}
});
Lastly, I want to show users how many items are in the list. THe HTML and styling for this is already included in our header.
.count
element.filterComplete
function means we can also return the number of items in the list after it has been filteredvar $count = $('.count')
$count.append(albums.size());
albums.on('filterComplete', function(){
$count.text(albums.update().matchingItems.length);
});
Putting all the code togther we can see our generated list of albums! You can get the full code on Codepen
See the Pen Sort and Filter Using List.js by Matthew Elsom (@matthewelsom) on CodePen.