In this lesson I’ll show you how to build a basic responsive and filterable portfolio powered by Bootstrap 3 and Shuffle.JS.
Shuffle.JS is a free alternative to the popular Isotope plugin.
Final Result:
Resources used
- Bootstrap 3
- Shuffle.JS
- jQuery throttle / debounce
- pictures from Lorempixel
- pictures from Lorempicsum
1. Download Bootstrap 3
Head to Bootstrap 3 and grab a copy. We’ll be using the grid system and a few list styles and paragraph classes.
Download and extract the archive in the folder project.
You’ll now have 3 folders: css, fonts, js. In the css folder add a custom.css file and add custom.js file in the js folder.
2. Bootstrap template and Shuffle.js plugin
Create an index.html file and copy & paste the content from Bootstrap’s Basic Template.
We also have to download the Shuffle.js plugin. After you extract the archive, go to the dist folder and copy the jquery.shuffle.modernizr.min to our project js folder.
Before we start coding, we just have to download one more javascript dependecy – jQuery throttle / debounce. Grab the minified file and paste it in our project’s js folder.
If you want, you can combine the minified jQuery throttle / debounce with the minfied shuffle.js and only use one file.
Now we have to add links to the custom.css, custom.js and shuffle.js. We’ll add the css in the head
and the js script in the footer
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <title>Filterable Portfolio with Bootstrap and Shuffle.js</title> <!-- Bootstrap --> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/custom.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <h1>Hello, world!</h1> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="js/bootstrap.min.js"></script> <script src="js/jquery.shuffle.min.js"></script> <script src="js/custom.js"></script> </body> </html> |
3. The Filter menu
Right bellow the body, add an h1 heading. To center align the text you can use Bootstrap’s special css class: text-center.
Now it’s time to create the Portfolio section. We need a section
element will contain the filter menu and the grid, container div and row div – part of the Bootstrap 3 grid, our filter menu is stored in an unordered list – ul
and 4 list items (li
) that will hold our menu options.
If you’re using Emmet plugin for your favorite code editor you can easily generate this by expanding the following code with the TAB key or CTRL+E:
section.portfolio>.container>.row>ul.portfolio-sorting.list-inline.text-center>li*4>a[href="#"]
The list-inline
class used in our unordered list is a Bootstrap 3 class that removes the list styling and displays the list items inline. Text-center
is another Bootstrap class used to center align the text in the parent container.
Now we have to add a data-group
to the links in our filter menu. Data-groups will act as a category, enabling us to filter our portfolio.
1 2 3 4 5 6 |
<ul class="portfolio-sorting list-inline text-center"> <li><a href="#" data-group="all" class="active">All</a></li> <li><a href="#" data-group="people">People</a></li> <li><a href="#" data-group="simpsons">Simpsons</a></li> <li><a href="#" data-group="futurama">Futurama</a></li> </ul> <!--end portfolio sorting --> |
4. The Portfolio items and grid
We have to display a list of images that represent the portfolio items. This means that we need an unordered list that will hold our list items.
One list item will have a data-groups
that will allow our plug-in to compare the filter menu item with the portfolio item and then filter it. Inside a list item we’ll have a figure
element that will hold our image.
A critical element that will allow us to filter our content is the sizer. The sizer will be used by the javascript plugin to find out the dimensions of our element. To add the sizer simply add another list item with the Bootstrap columns that you want to use in your portfolio items design.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<ul class="portfolio-items list-unstyled" id="grid"> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["people"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempixel.com/700/400/people/1" alt="Item 1" class="img-responsive"> </a> </figure> </li> <!-- sizer --> <li class="col-md-4 col-sm-4 col-xs-6 shuffle_sizer"></li> </ul> <!--end portfolio grid --> |
5. Adding more items to our portfolio
To easily add pictures, without having to edit them, I used 2 awesome image placeholder services that provide us with free images: Lorempixel and Lorempicsum.
To make sure that our images are responsive, don’t forget to add the Bootstrap 3 class img-responsive
to our images.
Here is the final portfolio section, with all the elements added:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
<section class="portfolio"> <div class="container"> <div class="row"> <ul class="portfolio-sorting list-inline text-center"> <li><a href="#" data-group="all" class="active">All</a></li> <li><a href="#" data-group="people">People</a></li> <li><a href="#" data-group="simpsons">Simpsons</a></li> <li><a href="#" data-group="futurama">Futurama</a></li> </ul> <!--end portfolio sorting --> <ul class="portfolio-items list-unstyled" id="grid"> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["people"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempixel.com/700/400/people/1" alt="Item 1" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["people"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempixel.com/700/400/people/7" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["futurama"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/futurama/700/400/1" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["futurama"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/futurama/700/400/2" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["simpsons", "people"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/simpsons/700/400/1" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["simpsons"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/simpsons/700/400/3" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["people"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempixel.com/700/400/people/9" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["simpsons"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/simpsons/700/400/4" alt="" class="img-responsive"> </a> </figure> </li> <li class="col-md-4 col-sm-4 col-xs-6" data-groups='["futurama"]'> <figure class="portfolio-item"> <a href="#"> <img src="http://lorempicsum.com/futurama/700/400/5" alt="" class="img-responsive"> </a> </figure> </li> <!-- sizer --> <li class="col-md-4 col-sm-4 col-xs-6 shuffle_sizer"></li> </ul> <!--end portfolio grid --> </div> <!--end row --> </div> <!-- end container--> </section> |
6. Adding the CSS
Now we have to style the grid elements. Since we are using the Bootstrap grid system, we don’t need to add special sizing to the items.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
body { background-color: #f1f5f8; border-top: 10px solid #2980b9; } .portfolio { margin: 48px 0; } .portfolio-sorting { text-transform: uppercase; font-size: 16px; margin-bottom: 48px; } .portfolio-sorting li a { color: #808080; text-decoration: none; padding: 6px; } .portfolio-sorting li a:hover, .portfolio-sorting li a.active { color: #2980b9; border-bottom: 2px solid #2980b9; } .portfolio-item { margin-bottom: 30px; } |
7. Adding JavaScript
Now that we finished the HTML and CSS part, it’s time to add functionality to our portfolio. We’ll base our code on the example code provided by the Shuffle.JS plugin in the demos folder.
In the custom.css we’ll begin by initializing the plugin:
1 2 3 4 |
$(document).ready(function() { shuffleme.init(); //filter portfolio }); |
Next we have to identify the target items: the grid, the filter menu and the item size.
1 2 3 |
var $grid = $('#grid'), //locate what we want to sort $filterOptions = $('.portfolio-sorting li'), //locate the filter categories $sizer = $grid.find('.shuffle_sizer'), //sizer stores the size of the items |
Here is the final JavaScript code in the custom.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
var shuffleme = (function( $ ) { 'use strict'; var $grid = $('#grid'), //locate what we want to sort $filterOptions = $('.portfolio-sorting li'), //locate the filter categories $sizer = $grid.find('.shuffle_sizer'), //sizer stores the size of the items init = function() { // None of these need to be executed synchronously setTimeout(function() { listen(); setupFilters(); }, 100); // instantiate the plugin $grid.shuffle({ itemSelector: '[class*="col-"]', sizer: $sizer }); }, // Set up button clicks setupFilters = function() { var $btns = $filterOptions.children(); $btns.on('click', function(e) { e.preventDefault(); var $this = $(this), isActive = $this.hasClass( 'active' ), group = isActive ? 'all' : $this.data('group'); // Hide current label, show current label in title if ( !isActive ) { $('.portfolio-sorting li a').removeClass('active'); } $this.toggleClass('active'); // Filter elements $grid.shuffle( 'shuffle', group ); }); $btns = null; }, // Re layout shuffle when images load. This is only needed // below 768 pixels because the .picture-item height is auto and therefore // the height of the picture-item is dependent on the image // I recommend using imagesloaded to determine when an image is loaded // but that doesn't support IE7 listen = function() { var debouncedLayout = $.throttle( 300, function() { $grid.shuffle('update'); }); // Get all images inside shuffle $grid.find('img').each(function() { var proxyImage; // Image already loaded if ( this.complete && this.naturalWidth !== undefined ) { return; } // If none of the checks above matched, simulate loading on detached element. proxyImage = new Image(); $( proxyImage ).on('load', function() { $(this).off('load'); debouncedLayout(); }); proxyImage.src = this.src; }); // Because this method doesn't seem to be perfect. setTimeout(function() { debouncedLayout(); }, 500); }; return { init: init }; }( jQuery )); $(document).ready(function() { shuffleme.init(); //filter portfolio }); |
The setupFilters function allows us to filter the portfolio by identifying the active filter menu item.
8. Additional HTML & CSS
For easy reference, I added the resources used in this tutorial in the footer. I placed the content inside 3 divs with col-md-4
class.
With Emmet you can quickly generate the HTML code by expanding this line of code: footer>.container>.row>.col-md-4.column*3>h4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<footer> <div class="container"> <div class="row"> <div class="col-md-4 column"> <h4>AcasA Programming</h4> <p>A tutorial by <a href="http://acasaprogramming.ro">AcasA Programming</a></p> <ul class="social-icons list-inline"> <li><a href="https://www.youtube.com/channel/UCBGItdbB-5Yma5X6FK_hYBA" class="youtube"><span class="fa fa-youtube"></span></a></li> <li><a href="https://www.facebook.com/acasaprogramming" class="facebook"><span class="fa fa fa-facebook"></span></a></li> <li><a href="https://github.com/acasaprogramming" class="github"><span class="fa fa-github"></span></a></li> </ul> </div> <div class="col-md-4 column"> <h4>Credits</h4> <ul class="list-unstyled"> <li><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><a href="https://getbootstrap.com/">Bootstrap 3</a></li> <li><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><a href="https://vestride.github.io/Shuffle/">Shuffle.js</a></li> </ul> </div> <div class="col-md-4 column"> <h4>Images by:</h4> <ul class="list-unstyled"> <li><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span><a href="http://lorempixel.com/">Lorempixel</a></li> <li><span class=" glyphicon glyphicon-chevron-right" aria-hidden="true"></span><a href="http://lorempicsum.com/">Lorempicsum</a></li> </ul> </div> </div> <!--end row --> </div> <!-- end container--> </footer> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/************** FOOTER ***************/ footer { border-top: 5px solid #2980b9; background-color: #34495e; padding: 24px; font-size: 16px; } footer, footer a { color: white; } footer a:hover { color: #2980b9; } footer .column ul li { margin-bottom: 6px; } footer .column h4 { border-bottom: 2px solid #2980b9; padding-bottom: 12px; margin-bottom: 24px; } footer .column .glyphicon-chevron-right { margin-right: 6px; color: #2980b9; } .social-icons { margin-bottom: 12px; } |
Congratulations
You’ve completed this tutorial and learned how to easily create and set-up a filterable grid powered by Bootstrap 3 and Shuffle.JS.
Thanks for reading!
Hi thank for your tutorial but i don’t understand what you are doing in this part of code
proxyImage = new Image();
$( proxyImage ).on(‘load’, function() {
$(this).off(‘load’);
debouncedLayout();
});
proxyImage.src = this.src;
});
I’m not an expert in jquery but what i understand is that you’re looking if some image are on load and you stop this load with $(this).off(‘load’); that means that some image won’t be load
Why are you doing that ?
hey,
is there any way of setting the filter load default? so instead of it loading up with all the images, setting it so the page loads just the futurama pics?
Thanks
Terrible
Great article! We are linking to this particularly great post on our website.
Keep up the great writing.
Have you ever thought about creating an e-book or guest authoring
on other blogs? I have a blog centered on the same topics you discuss and would love to have you share some stories/information. I know my viewers would value
your work. If you’re even remotely interested, feel free to shoot me an e-mail.
What’s up, for all time i used to check web site posts here in the early hours in the morning, because i love to find out more and
more.
This is a nice design please try it……
link:http://www.nicesnippets.com/snippet/image-gallery-with-filter-using-bootstrap-and-jquery
Hello there! Quick question that’s entirely off topic.
Do you know how to make your site mobile friendly? My web site looks weird when browsing from my iphone 4.
I’m trying to find a theme or plugin that might be able
to correct this issue. If you have any recommendations, please
share. Thanks!
I have to thank you for the efforts you’ve put in writing this blog.
I really hope to see the same high-grade blog posts from you in the future as well.
In fact, your creative writing abilities has inspired me
to get my own, personal blog now 😉
I am using this on my site. Is there a way to link directly to things (like #animals)? Also can you sort by date?
Thanks for your article on Shuffle.js Very helpful.
PS: By the way, in my early days I was a big fan of the Romanian company known as InterAkt. They created a collection of PHP 4 tools which I used to create my first Content Management System.
InterAkt developed the tools for Macromedia Dreamweaver. Both Macromedia and InterAkt were acquired by Adobe, and like all good things acquired by Adobe, they both suffered. InterAkt’s products were dropped and Dreamweaver has also declined in marketshare. Who would use Dreamweaver these days ???