Using jQuery to filter results by multiple tags

The problem

How do we let users use a tag cloud using multiple choices to filter through my design portfolio.

tag filters in the wild

The screencast

The Concept

This tutorial, PART TWO, is geared towards the Revolution version of MODX, but it would easily work in EVO by substituting the Ditto snippet for get Resources. You'll notice below code blocks, I've included the EVO version of the code. Tip Click on Show Evo Code to see the code.

The big goal for this turtorial is to move from one tag cloud to multiple tag lists, each of which can filter the portfolio list below. I want getResources to create my portfolio of list items inside an unordered list. Each list item will have a bunch of class names attached to it. If the user clicks a tag in the tag lists above, any list item that has that class will be displayed, and any list item that doesn't, will disappear. Cool Right?

Code

The actual markup I used was:

 
<h1>Check Out My Work</h1>
<p><span class="blue button">Filter Portfolio by clicking on tags </span> <span class="blue button" style="background-color: purple;"> → Then Click on the thumbnails for more info about each project.</span></p>
<ul id="portfolio-list" class="clearfix span-24">[[getResources? &amp;tpl=`portfolio1` &amp;parents=`26` &amp;limit=`0` &amp;includeTVs=`1` &amp;processTVs=`1` &amp;tvPrefix=`` &amp;sortbyTV=`mydate` &sortdir=`DESC`]]</ul>
<div id="portfolioCuteness" class="span-20" style="display: none;">
<h2>I haven't done a project with those requirements yet, but it sounds intriguing, Maybe we should talk? <br /> <br /> <a class="blue button" href="mailto:noahlearner@gmail.com">email me</a>  or   <span class="purple button" onclick="reset();">reset list</span></h2>
</div>

Show Evo Code

<h1>Check Out My Work</h1>
<p><span class="blue button">Filter Portfolio by clicking on tags </span> <span class="blue button" style="background-color: purple;"> → Then Click on the thumbnails for more info about each project.</span></p>
<ul id="portfolio-list" class="clearfix span-24">[!Ditto? &amp;tpl=`portfolio1` &amp;parents=`26` &amp;display=`all` &amp;removeChunk=`Comments` &amp;orderBy=`tvmyDate DESC` !]</ul>
<div id="portfolioCuteness" class="span-20" style="display: none;">
<h2>I haven't done a project with those requirements yet, but it sounds intriguing, Maybe we should talk? <br /> <br /> <a class="blue button" href="mailto:noahlearner@gmail.com">email me</a>  or   <span class="purple button" onclick="reset();">reset list</span></h2>
</div>

The portfolio1 chunk is:

 
<ul>
<li class="[[+Discipline]]#[[+Industry]]"><a href="[[~[[+id]]]]" target="_blank" rel="colorbox"><img src="[[+homeImg]]" alt="[[+pagetitle]]" /><br />
<strong>visit [[+jobTitle]]</strong></a><br /><br /></li>
</ul>

Show Evo Code

<ul>
<li class="[+Discipline]#[+Industry]"><a href="[~[+id+]~]" rel="colorbox" target="_blank"><img src="[+homeImg+]" alt="[+pagetitle+]" /><br /> <strong>visit [+jobTitle+]</strong></a><br /><br /></li>
</ul>

Notice the

 class="[[+Discipline]]#[[+Industry]]"

This is where the magic happens. This is what creates the two filterable lists.

In each of the portfolio documents, I have a two COUNT THEM TWO set up called "Discipline" and "Industry". They are the auto-tag Input type, with default output. I then enter all the tags that I want the user to be able to sort by.

If you have a two word tag, separate the two words with an underscore(_) when you create the tags in the TV. Use the underscore because it is unlikely that you would ever need a tag that includes an underscore in name. The underscore will be removed before output by jQuery.This will keep the tags intact. Otherwise the javascript will break up the words and create two separate tags.

On the page we make the snippet call and then before the closing </body> tag we make sure to include jQuery followed by our custom code.

 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="[[++base_url]]assets/js/jquery.colorbox-min.js"></script>

Show Evo Code

 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" src="[(base_url)]assets/js/jquery.colorbox-min.js"></script>
<script type="text/javascript"> 
jQuery(document).ready(function($) {
	$("#portfolio-list a").colorbox({
		iframe: true,
		innerWidth: 685,
		innerHeight: 440
	});
	var i, len, cla, cat; 
	classListin = [];
	categoryListin = [];
	splitList = [];
	$("#portfolio-list li").map(function() { 
		var $this = $(this);
		classes = $this.attr('class');
		$this.removeClass();
		$(this).addClass(classes.split('#').join("  ").split(',').join(" "));
		splitList.push(classes.split('#'));
	}); 
	for (i = 0, len = splitList.length; i < len; i += 1) {
		cla = splitList[i][0];
		cat = splitList[i][1];
		classListin = classListin.concat(cla.split(','));
		categoryListin = categoryListin.concat(cat.split(','));
	}
	classes = classListin;
	categoryList = distinctList(categoryListin);
	classList = distinctList(classes);
	var tagList = '<ul class="tag-list"></ul>';
	tagItem = '<h2 class="industry">Discipline</h2><span class="reset purple button" onclick="reset();">RESET</span>';
	tagFilter = '';
	$.each(classList, function(index, value) {
		TagText = value.split(','); 
		TagText = value.split('_').join(" ");
		tagItem += '<li id="' + value + '" ><a href="#" onclick="return false;">' + TagText + '</a></li>';
	}); 
	$("#portfolio-list").before($(tagList).append(tagItem));
	$("# thirdTier.tag-list").before($(tagFilter));
	tagItem3 = '';
	$.each(categoryList, function(index, value) {
		TagText7 = value.split(',');
		TagText7 = value.split('_').join(" ");
		tagItem3 += '<li  id="' + value + '" ><a href="#" onclick="return false;">' + TagText7 + '</a></li>';
	});
	$("#portfolio-list").before($(tagList).append('<h2 class="industry">Industry</h2>' + tagItem3));
	$("#thirdTier .tag-list").before($(tagFilter)); 
	$('#thirdTier .tag-list li').live('click', function(el) {
		$(this).toggleClass('active');
		var choice = $.map($(".active"), function(el) {
			return "." + el.id;
		}).join("");
		if (choice) {
			$("#portfolio-list").hide();
			$("#portfolio-list li").hide();
			$("#portfolio-list li" + choice).show();
			$("#portfolio-list").fadeIn(400);
			var portLength = $("#portfolio-list li:not(" + choice + ")").length;
			var portTotal = $("#portfolio-list li").length;
			if (portLength == portTotal) {
				$("#portfolioCuteness").fadeIn(400);
			}
			if ((portLength !== portTotal) && ($('#portfolioCuteness').length)) {
				$('#portfolioCuteness').hide();
			}
		} else {
			$("#portfolio-list li").show(400);
		} // prevent the page scrolling to the top of the screen
		return false;
	});
}); 


function distinctList(inputArray) {
	var i;
	var length = inputArray.length;
	var outputArray = [];
	var temp = {};
	for (i = 0; i < length; i++) {
		temp[inputArray[i]] = 0;
	}
	for (i in temp) {
		outputArray.push(i);
	}
	var output = outputArray.sort();
	return output;
}
function reset() {
	$('#portfolioCuteness').fadeOut(200);
	$(".tag-list li.active").removeClass('active');
	$("#portfolio-list li").fadeIn(400);
}
</script>

If it is over your head, you can pretty much cut and paste what I provide. You will also be very well served by installing firebug, which is a firefox add-on and by inspecting the source code on my home page to see how it all works together. Otherwise, bear with me.

So what is going on here?

The javascript code looks at the unordered list items created by the getResources call.

It then looks at all the different class names and creates a distinct list of classes in the function named distinctList. It sorts this list alphabetically.

It then outputs the tag cloud in an unordered list with an id of tag-list.

The first list item is the reset list item. When the user clicks that it resets the tag cloud and displays all the portfolio items by enacting the reset function.

When user clicks on a list item, other than the reset, it toggles the active class for that item. It then adds the active list class to map of checked items, and displays the list items that match this variable that is called "choice".

If the link in the ul#portfolio-list is not in choice it is hidden.

Is this not sweet?

I know there are many ways to refactor the code. I'm hoping that you can help do that in the comments below.

Enjoy! and I'd love any feedback you might have.

Add Your Comment

Comments powered by LudwigDisqus for ModX