jQuery

Creating a “Filterable” Portfolio with jQuery

Tutorial Details
  • Technology: jQuery, CSS
  • Difficulty: Intermediate
  • Completion Time: 1-2 hours

If you have worked in your field for a while, there is a pretty good chance that you have a rather extensive portfolio. To make it a little easier to navigate, you will probably be tempted to break them into different categories. In this tutorial, I will show you how to make “filtering by category” a little more interesting with just a little bit of jQuery.

Final Product

1. The Markup

Our portfolio is nothing more than a simple unordered list:

<ul id="portfolio">
	<li><a href="#"><img src="images/a-list-apart.png" alt="" height="115" width="200" />A List Apart</a></li>
	<li><a href="#"><img src="images/apple.png" alt="" height="115" width="200" />Apple</a></li>
	<li><a href="#"><img src="images/cnn.png" alt="" height="115" width="200" />CNN</a></li>
	<li><a href="#"><img src="images/digg.png" alt="" height="115" width="200" />Digg</a></li>
	<li><a href="#"><img src="images/espn.png" alt="" height="115" width="200" />ESPN</a></li>
	<li><a href="#"><img src="images/facebook.png" alt="" height="115" width="200" />Facebook</a></li>
	<li><a href="#"><img src="images/google.png" alt="" height="115" width="200" />Google</a></li>
	<li><a href="#"><img src="images/netflix.png" alt="" height="115" width="200" />Netflix</a></li>
	<li><a href="#"><img src="images/nettuts.png" alt="" height="115" width="200" />NETTUTS</a></li>
	<li><a href="#"><img src="images/twitter.png" alt="" height="115" width="200" />Twitter</a></li>
	<li><a href="#"><img src="images/white-house.png" alt="" height="115" width="200" />White House</a></li>
	<li><a href="#"><img src="images/youtube.png" alt="" height="115" width="200" />YouTube</a></li>
</ul>

Note: I was by no means a part of creating these wonderful sites; I am just using them as examples.


2. Categorizing the Portfolio

We are going to assume that our portfolio can be broken down into 5 categories:

  • Design
  • Development
  • CMS
  • Integration
  • Information Architecture

In order to use the categories we have defined, we will convert them to lowercase and replace all spaces with hyphens:

  • Design = design
  • Development = development
  • CMS = cms
  • Integration = integration
  • Information Architecture = information-architecture

We are going to assume that each portfolio item could be in one or many categories, so we are going to randomly add our newly created categories as classes to the list items:

<ul id="portfolio">
	<li class="cms integration">
		<a href="#"><img src="images/a-list-apart.png" alt="" height="115" width="200" />A List Apart</a>
	</li>
	<li class="integration design">
		<a href="#"><img src="images/apple.png" alt="" height="115" width="200" />Apple</a>
	</li>
	<li class="design development">
		<a href="#"><img src="images/cnn.png" alt="" height="115" width="200" />CNN</a>
	</li>
	<li class="cms">
		<a href="#"><img src="images/digg.png" alt="" height="115" width="200" />Digg</a>
	</li>
	<li class="design cms integration">
		<a href="#"><img src="images/espn.png" alt="" height="115" width="200" />ESPN</a>
	</li>
	<li class="design integration">
		<a href="#"><img src="images/facebook.png" alt="" height="115" width="200" />Facebook</a>
	</li>
	<li class="cms information-architecture">
		<a href="#"><img src="images/google.png" alt="" height="115" width="200" />Google</a>
	</li>
	<li class="integration development">
		<a href="#"><img src="images/netflix.png" alt="" height="115" width="200" />Netflix</a>
	</li>
	<li class="information-architecture">
		<a href="#"><img src="images/nettuts.png" alt="" height="115" width="200" />NETTUTS</a>
	</li>
	<li class="design information-architecture cms">
		<a href="#"><img src="images/twitter.png" alt="" height="115" width="200" />Twitter</a>
	</li>
	<li class="development">
		<a href="#"><img src="images/white-house.png" alt="" height="115" width="200" />White House</a>
	</li>
	<li class="cms design">
		<a href="#"><img src="images/youtube.png" alt="" height="115" width="200" />YouTube</a>
	</li>
</ul>

Adding Category Navigation

Now that we have the portfolio pieces in place, we are going to need some way to navigate through them. Another unordered list should do:

<ul id="filter">
	<li class="current"><a href="#">All</a></li>
	<li><a href="#">Design</a></li>
	<li><a href="#">Development</a></li>
	<li><a href="#">CMS</a></li>
	<li><a href="#">Integration</a></li>
	<li><a href="#">Information Architecture</a></li>
</ul>

Since I want the default view of the portfolio to show All items, I have assigned a class of current to the first list item.

You will probably look at that and question me on the accessibility of this example. My thought is that you have 3 options to solve that problem.

  1. When creating a portfolio like this, there is a strong probability that it will be database driven. Thus, you should be able to create a separate page for each category. So if a user does not have JavaScript enabled, they can go to the separate page with the filtered portfolio.
  2. You can use a similar technique from my last tutorial: setting a parameter in the url.
  3. You could always just write in the navigation with JavaScript before the portfolio items:
    $(document).ready(function() {
    	$('ul#portfolio').before('<ul id="filter"><li class="current"><a href="#">All</a></li><li><a href="#">Design</a></li><li><a href="#">Development</a></li><li><a href="#">CMS</a></li><li><a href="#">Integration</a></li><li><a href="#">Information Architecture</a></li></ul>');
    });

Ok, you’ve got my notes on accessibility, so don’t criticize me for not thinking about it.


3. The CSS

This tutorial is not meant to be about CSS, so I’m going to run through the CSS pretty quickly.

I always start with some basic styles as a sort-of framework, so I’m not going to go over those styles right now. These styles basically just act as a reset and define some styling for basic elements.

To start, I just want to display the categories across the top horizontally with a border between each:

ul#filter {
	float: left;
	font-size: 16px;
	list-style: none;
	margin-left: 0;
	width: 100%;
}
ul#filter li {
	border-right: 1px solid #dedede;
	float: left;
	line-height: 16px;
	margin-right: 10px;
	padding-right: 10px;
}

Next, I want to remove the border from the last list item (in browsers that support it) and change the display of the links:

ul#filter li:last-child { border-right: none; margin-right: 0; padding-right: 0; }
ul#filter a { color: #999; text-decoration: none; }

I also want to make sure and differentiate the currently selected category:

ul#filter li.current a, ul#filter a:hover { text-decoration: underline; }
ul#filter li.current a { color: #333; font-weight: bold; }

Ok, now that we have the category navigation styled, let’s focus on the actual layout of the portfolio. Let’s plan on floating 3 list items next to each other with a border around each one:

ul#portfolio {
	float: left;
	list-style: none;
	margin-left: 0;
	width: 672px;
}
ul#portfolio li {
	border: 1px solid #dedede;
	float: left;
	margin: 0 10px 10px 0;
	padding: 5px;
	width: 202px;
}

Now, we just need to add some basic styling for the images and links:

ul#portfolio a { display: block; width: 100%; }
ul#portfolio a:hover { text-decoration: none; }
ul#portfolio img { border: 1px solid #dedede; display: block; padding-bottom: 5px; }

Compensating for Internet Explorer 6

Of course, let’t not forget about our friend IE6. Once you start clicking through some of the filters, the layout gets a little crazy.

IE Screenshot

From what I can tell, it is the dreaded IE Double Margin bug. I tried applying display: inline to the list items once they are filtered, but that didn’t seem to fix it. So my best solution was to just halve the right margin:

ul#portfolio li { margin-right: 5px; }

We are of course only going to serve this IE6 specific stylesheet using conditional comments:

<!--[if lt IE 7]>
<link href="stylesheets/screen-ie6.css" type="text/css" rel="stylesheet" media="screen,projection" />
<![endif]-->

Yeah, it doesn’t look as good, but you know what: I don’t care. If you are using IE6, you deserve it.


4. The jQuery

Ok, now that we have the markup and CSS all done, let’t get to the important part of this tutorial: the JavaScript.

We are going to start by including the latest version of jQuery in the head of our document.

<script type="text/javascript" src="scripts/jquery.js"></script>

Next, we want to execute our code once the document is loaded.

$(document).ready(function() {

});

Now, we don’t want to do anything until one of our categories is clicked. We also want to make sure that we do not follow the href value of the link, so we need to return false:

$('ul#filter a').click(function() {
	return false;
});

Once a category link is clicked, I want to do a couple of things: remove the outline on the clicked link, remove the class of current on the list item that has it, and add the class of current on the parent of the clicked link:

$(this).css('outline','none');
$('ul#filter .current').removeClass('current');
$(this).parent().addClass('current');

Next, we want to get the text inside of the clicked link, convert it to lowercase, and replace any spaces with hyphens (just like before when we were creating the category classes):

var filterVal = $(this).text().toLowerCase().replace(' ','-');

The first case of the script is when the All link is clicked. When that is clicked, we want to show all of the portfolio items and remove the class of hidden:

if(filterVal == 'all') {
	$('ul#portfolio li.hidden').fadeIn('slow').removeClass('hidden');
}

Otherwise, one of the actual categories were clicked. So we want to go through each portfolio item and check to see if it has the class that equals the value of the category clicked. If it does not have the class, we want to fade out the list item and add a class of hidden. It it does have the class, we want to fade it in and remove the class of hidden:

else {
	$('ul#portfolio li').each(function() {
		if(!$(this).hasClass(filterVal)) {
			$(this).fadeOut('normal').addClass('hidden');
		} else {
			$(this).fadeIn('slow').removeClass('hidden');
		}
	});
}

The Finished Script

Let’s take a look at the entire script:

$(document).ready(function() {
	$('ul#filter a').click(function() {
		$(this).css('outline','none');
		$('ul#filter .current').removeClass('current');
		$(this).parent().addClass('current');

		var filterVal = $(this).text().toLowerCase().replace(' ','-');

		if(filterVal == 'all') {
			$('ul#portfolio li.hidden').fadeIn('slow').removeClass('hidden');
		} else {
			$('ul#portfolio li').each(function() {
				if(!$(this).hasClass(filterVal)) {
					$(this).fadeOut('normal').addClass('hidden');
				} else {
					$(this).fadeIn('slow').removeClass('hidden');
				}
			});
		}

		return false;
	});
});

Some people may not like the effect, but I think it looks pretty cool how they all kind of dance around. This is definitely not the only way to accomplish something like this, and it could easily be built on to do other things.

This technique is actually evolved from the coding that I did for my company’s portfolio.

Final Product

5. One Quick Note

You may have noticed that I was adding and removing the class of hidden on the items as I was toggling the visibility. While I didn’t end up doing anything with the class, I try and make it a habit to add and remove classes to denote the state they are in. You may not use it immediately, but it can provide a hook for you do stuff with in the future.

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Add Comment

Discussion 262 Comments

Comment Page 4 of 5 1 2 3 4 5
  1. ilz says:

    How would i link to a section? For example, say from my home page i wanted to link directly to the “design” section?

  2. Liam says:

    I don’t think anyone offered an answer to this but can someone explain how to link to a certain displayed category from an external page?

    E.G. jumping straight to the ‘design’ ones being shown rather than to the default ‘all’.

    • Liam says:

      I think I may have worked this out myself(?) I believe it should work if your links text matches (e.g.) ‘design’ and you link to the page with a hash;

      href=”index.html#” ..I think.

    • Henrique Erzinger says:

      in that code it’s impossible.. To make it possible you would have to modify it a bit… It can be done using the “#” and anchor links, I think. I and a friend used this technique to make possible the use of the history.back (and consequently the browsers back button) in pages loaded via ajax.

      I would have to think more about it… If I come to actually use it and figure the way out, I’ll post the solution.

    • Pedro Cardoso says:

      I’m having issues with this situation also, could someone give some pointers on how to solve this problem?

  3. alex says:

    hey i was just wondering..is there any way to remove the outline around the images? I don’t have any text so there isn’t an outline there but there’s always that grey outline

    …I’ve tried deleting all different sections of the files but still can’t find it.

  4. Kim Guanzon says:

    has anyone solved how to escape/replace special characters on this… also, if i go more than 3 words on the main list, the portfolio UL does not switch.

    • Andy says:

      Hey Kim. I just added .replace(‘ ‘,’-') to the end of the first .replace(‘ ‘,’-') so the second space is replaced with a hyphen in a 3 word category.

      For example, “Decks and Patios” was only getting adjusted to “decks-and patios”. New replace code is: var filterVal = $(this).text().toLowerCase().replace(‘ ‘,’-').replace(‘ ‘,’-');

      • Arturro says:

        You should change this:

        var filterVal = $(this).text().toLowerCase().replace(‘ ‘,’-');

        to this:

        var filterVal = $(this).text().toLowerCase().replace(‘ ‘,”);

        because when You have categories like: Web Design and Print Design – Youll get a mismatch of the results. I know the dash is nice but it need to be functional, not nice.

        anybody integrated a pagination to this script?

  5. JulioD says:

    Awsome… I`ll be using it in my personal site.

    Thanks

  6. yeecheing says:

    It’s really nice. i try it in my portfolio too.. If the content can make it in pagination should be nice. Because of when i refresh the page will show All item.. but the Item too long. But anyway this is really nice, Thanks a lot.

  7. This is dope. says:

    The best part about this:
    Yeah, it doesn’t look as good, but you know what: I don’t care. If you are using IE6, you deserve it.

    Epic.

    Thanks and F.U. IE6 users.

  8. Michael says:

    Hi there, I am using this script for my photography portfolio, but i have a little prob.
    At the entry I want to show just one category, not the whole thing. Is this possible in any way?
    It would be very nice, if you have an answer for me.

    greetings,
    michael

    (link of the page above)

  9. Chad says:

    so what are u using for a trigger? I see you defined your categories but i’m not seeing how you tied them back together. Can you assist?

  10. Chad says:

    go it figured out. Thanks.

  11. Jason says:

    Is it possible to do this with divs?

    I’m using masonry for layout, and would like to filter the div items.

    Thoughts?

  12. Jason says:

    Unfortunately this does not work with easy-list-splitter. It will only work with the first column but not the others. Shucks…

  13. Rutu says:

    Great tutorial!
    Though I am unsure as to how I could rename CMS to say – Web? I know I can rename the link how how do I get it to work if I change the class name in the list. Make sense?

    Maybe I missed something in the tutorial.

    Thanks

  14. Kalle says:

    It doesn’t work with jquery-1.4.2.min.js.
    Any ideas why?

  15. Missy says:

    Has anyone figured out a way to make this work with Lightbox? Once the lightbox JS files are referenced, the filters no longer work on the page.

    • Andre says:

      I am also having trouble integrating the lightbox script. When I add the script, the filtering links don’t work anymore

      Can anyone help? I see that Travis managed to get it working on his website… can we have a look at the code?

  16. Josh Child says:

    This is a great tutorial by the way! Can you direct me or give me a hint how to simply dim out the thumbs that are not part of the sorting process instead of disappearing. I want my users to be able to still see the other thumbs there and only sort by dimming them. Make sense?

  17. Shaz says:

    Hi,

    Great tutorial,

    I would like to know how to do a couple of things:

    How do open it so it the first page displays a speciifed category and not all items?
    How can assign mulitple classes to each category, so I can show specific items across mulitple categories within it’s own category?

    Any help on this matter would be greatly apreciated.

    thank you

  18. Amy says:

    The replace(‘ ‘,’-'); function only replaces the first space and not all of them with hyphens. How do you fix this?

  19. Lucas says:

    excellent , i’ve just added to my script list
    http://www.ajaxshake.com
    regards

  20. Josip says:

    Hi,
    how to put this all in a wordpress post ? Made the images of games and a link to the game.
    Sorry my english is bad :-(

  21. Jason Day says:

    I’m trying to set this up with wordpress, but I’m getting “$ is not a function” line 75 in the code.

    Thoughts?

    • Katie says:

      You need to replace the “$” with “jQuery”. WordPress uses the dollar sign symbol for one of its own internal libraries, so you need to type out jQuery instead instead of using the shortcut.

  22. Marcio says:

    It’s a great way to show your portfolio. You can even add something like lightbox to make it really pop. For anyone looking at adding that, use a jQuery replica like ShadowBox and it will work perfectly.

  23. Reejo says:

    Guys Is there is a way to change the link on each item depending on the category selected.

    because i want to show just one icon and i have two types of websites for them. so depending on which category they selected, i want it to show a link. can some help me here.

  24. Dom says:

    Thanks so much for posting this, I tweaked the effects and will use it on my portfolio. Should be within 12 days!
    http://www.domanique.com

  25. Girish Sharma says:

    does nyone know, how to stay a div at its place while fading others down at their place or if hovered on one show content on next…

    • Kristian says:

      You can do that by changing the jQuery function in the script. Instead of using fadeOut(‘normal’) use a different jQuery call, for instance one that adjusts opacity down. (fadeTo() would work for instance).
      The reason they jump around is because they are hidden from the list flow with the fadeOut. If you just adjust opacity down, they stay in the flow and just become transparent. I’m using that very function in my portfolio.

      If anyone knows what to add to get linkable category filters, that would be pretty awesome… ;)

  26. mike i says:

    Thanks for the code.

    I edited the CSS and used it for our website portfolio, check it out: http://sonoradesignworks.com/website-portfolio.html

  27. Aaron says:

    Any way to have it use the .length and display the count next to each catagory/filter name?

    Example

    All (25) | Some Filter (10) | Another Filter (5) | And Another (15)

    Can someone show me how to modify the code to do this? Any help would be greatly appreciated!

  28. Beben Koben says:

    so interesting…
    thanks for explanation :thumbsup:

  29. I found a way to make you reach different categories via links using PHP.

    See the result here http://dustydeco.com/ourstuff.php

    You notice it when you click a item, you are then “in” the correct category!

  30. Steven C says:

    Is it posible to use more then one space when naming the filter. So for instance Right now, if i tried this:

    All
    Design
    Content Management System

    Design 1

    content-mangment-system

    It looks like when you have more than 1 space, the hyphen doesn’t work.

    Any help would be appreciated.

    Thanks,
    Steve C

  31. Steve C says:

    I actually found a solution for the space to hyphen issue.

    Find inline:
    var filterVal = $(this).text().toLowerCase().replace(‘ ‘,’-');

    and replace with:
    var filterVal = $(this).text().toLowerCase().replace(/ /g,’-');

    And it works perfectly.

    Example: http://nightmare-studio.com/video_section.php

    Thanks,
    Steve C

  32. Daniel says:

    For anybody that has been trying to figure out how to get the selected (current state) category to show up on load.

    say for example you have the navigation

    websites | logos | Cms |

    and you just want websites to load when the page loads

    simply just add the

    Websites

    Logos // this makes the div disaper on load but will reaper when you click logos.

    Cms
    // this makes the div disaper on load but will reaper when you click logos.

    this makes since cause the j query toggles the display when you click the corresponding category.

  33. Daniel says:

    For anybody that has been trying to figure out how to get the selected (current state) category to show up on load.

    say for example you have the navigation

    websites | logos | Cms |

    and you just want websites to load when the page loads

    simply just add the

    Websites

    Logos // this makes the div disaper on load but will reaper when you click logos.

    Cms

    // this makes the div disaper on load but will reaper when you click logos.

    this makes since cause the j query toggles the display when you click the corresponding category.

  34. Alidad says:

    I was wondering if this will works with mysql to retreive images from database using images file path name instead of writing whole of list of images in the code!

    does anyone have done this!

    AM

  35. Michael says:

    To filter a certain category on load I have…

    1) Set the “current” list item to the category that I wanted.
    2) Added this code to the page:

    $(window).load(
    function() {
    $(‘li.current a’).click();

    }
    );

    This does trigger the function and the jQuery from this tutorial does loop through each item that I want to hide, but they are still visible.

    Clicking on the filter item with the mouse works perfectly but triggering the “click” with javascript, while still calling the function, does not actually hide the portfolio items.

    Thoughts?

  36. Michael says:

    I am using jQuery to trigger a click event on one of the filter categories. In my debugging I’ve found that triggering the click this way does call the function, and the function does iterate through the portfolio items that it’s supposed to.

    The issue is that those items are not being hidden. Clicking on the filter category with the mouse works perfectly but I can’t figure out why triggering the click with jQuery is any different (once again, it DOES fire the function)

  37. Moonwalker says:

    Great tutorial.

    One question if I may: Is it possible to use DIVs instead of a list item?

    Thanks for the great tutorial.

    Keep it up.

    Moonwalker

  38. Thanks Dude. Very nice. I was looking for the same.

  39. Nimit says:

    For more than one space issue,

    replace below:
    var filterVal = $(this).text().toLowerCase().replace(‘ ‘,’-');

    with:
    var filterVal = $(this).text().toLowerCase().replace(/ /g,’-');

  40. Vince says:

    I really need to figure our how to filter categories upon loading. I’ve tried just about all theories in the comments and none seem to work.

    Any more suggestions?

  41. Ali says:

    hi i was wondering is there a way to save the current filter state so i could apply it to new data that is loaded. my site http://www.deen-il-islam.com i think i you use jquery cookies plugin to do this but can not figure out how to implement it. can any one help thanks.

  42. Vitor Melo says:

    I had already done something similar, however it can get better by using a paging example with AnythingSlider. Except that so far do not know how to organize the elements themselves after being hidden.

  43. Vitor Melo says:

    Esse tipo seria mais flexivel: http://pexeto.com/dandelion_wp/three-columns-2/
    Integrando categorias com paginação, alguém tem alguma sugestão?

  44. Vitor Melo says:

    This type is more flexible: http://pexeto.com/dandelion_wp/three-columns-2/
    Integrating categories with paging, does anyone have any suggestions?

  45. s3am says:

    Hi And thanks for this nice script.

    I’m trying to deal with accentuated characters (à, é, ô, ü …) ; tried this but dosen’t work

    Below the code fragment, is intended to lowercase, replace spaces with hyphens and replace é, è or ë with e
    So far everything works fine excepts accentuated e replacement.

    var regSpace = new RegExp(‘[ ]‘, ‘gi’);
    var regAccentE = new RegExp(‘[éèë]‘, ‘gi’);
    var filterVal = $(this).text().toLowerCase().replace(regSpace,’-').replace(regAccentE,’e');

    Any help would be much appreciated.

  46. lavergara says:

    I just want to say. I really appreciate that you break every part of the code down and explain everything that is going on. I am a designer learning to develop and because of your tutorial I was able to easily edit the code and apply it to my project while learning some jquery.

    There are a lot of websites that just post the code and I have to spend an enormousness amount of time researching in order to figure it all.

    Thank you for taking the time and thinking of some of us who are not so savvy when it comes to coding.

  47. Hi there,

    Has anyone got any tips on using masonry and superfish for sub category filtering at all?

    I’m using this technique with masonry and just looking at superfish integeration to use on sub category drop downs.. some clients are needing the ability to filter on a sub level if they wish….

Comment Page 4 of 5 1 2 3 4 5

Add a Comment

To add a code snippet to your comment, please wrap your code like so: <pre name="code" class="html">YOUR CODE</pre>. You can replace the class name with "js," "css," "sql," or "php." If there are any "<" or ">" within your code, please search and replace them with: &lt; and &gt; respectively.