5. Alphabetical List View Quick Filter

Update: August 15th, 2010

This article the first of The Power of Content Editor WebPart series. Other parts:

 

This is an easy kind-of hack where we’ll use SharePoint’s URL. Our goal is to have a small section above the list view that will filter the list according to first letter of every item in the list as seen on the screenshot below.

End result

This comes in handy with large lists. In our example we’ll continue to work on the list of customers that we’ve already "pimped" with the quick search functionality.

Let’s take a look at filters

For this purpose we’ll take a look at our list – what happens when we use the list filter:

Applying the filter   Check the URL parameters of an applied filter

The page URL gets additional parameters: View, FilterField1 and FilterValue. For working in list view you can easily skip the View parameter and the filter will still work.

Filter without "View" URL parameter

So FilterField1 determines according to which column the list should be filtered (we have to use column internal names) and FilterValue1 parameter sets the filter value. And yes, since they both have number 1 added to them, you can add multiple filters (FilterField2, FilterValue2, FilterField3, …).

 

Let’s do it

Great. So now we can take advantage of this. Because we don’t want to have each unique value in the filter options we need an additional column with the first name of a customer. The column must be displayed in the view in order for filter to work. For the column to be as discrete as possible, I recommend using a short column name, something like FL (first letter). Let’s go ahead and add the column.

Add a new column

The column should be of type "Calculated" and the formula is simple:

=LEFT([LastName])

The value in square brackets is the display name of the column. When entering the formula the square brackets should also be present. All the settings for a new column are presented below:

Column settings 

And as mentioned the column should be in the view where we’ll add the filter.

The new column added

Time for CEWP and JavaScript

OK. Now let’s insert the CEWP and JavaScript. After insertion, and linking to the external content file the "Hello world" is ready:

CEWP added   Content file with Hello world test

We could do this entirely without the JavaScript. All we’d need is to enter hyperlinks like below:

<a href="AllItems.aspx?FilterField1=FL&FilterValue1=A">A</a>

<a href="AllItems.aspx?FilterField1=FL&FilterValue1=B">B</a>  <a href="AllItems.aspx?FilterField1=FL&FilterValue1=C">C</a>

And that would just work fine.

Filter with pure HTML Pure HTML solution

But going on the wild side and to raise the complexity of this article I want to make this a bit more dynamic and page-independant. In the code above the problem is that this is bound to the AllItems.aspx page. What if you’d want to re-use the code somewhere else? Plus you have to do a lot of copy-paste and corrections for minor items.

So JavaScript to the rescue. To avoid repetitive typing we’ll create a JavaScript array of all possible values for filter and build the links in a for loop. So first let’s define an array of possible filter values.

<script type="text/javascript">
	var filterValues = new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
</script>

Next we create a loop for all of the values to create the filter just before the </script> tag:

for (var i = 0; i < filterValues.length; i++) {
	document.write("<a href="" + document.location.pathname + "?FilterField1=FL&FilterValue1=" + filterValues[i] + "">" + filterValues[i] + "</a>&nbsp;");
}

And that would do the trick. We’d end up with a filter:

image

By using the document.location.pathname in the script we’ve removed strict binding to any particular page.

That’s it. If you’re satisfied with a filter like that you can stop reading right here. If you wish to "pimp" the filter a bit more, continue reading.

Expanding our example

Now let’s add a bit more configurability. We wish to be able to configure a delimiter between the filter values and we wish to be able to configure the filter column. So immediately after the first line (<script type….) add the following two lines:

	var filterField = "FL";
	var filterValuesDelimiter = " : ";

And we’ll modify the for loop to be writing links into a new array instead of directly to the document.  So we modify the loop to take into account the filter field and take out the &nbsp; delimiter at the end. At the end we’ll use a join function for an array of links to add the delimiter between and write to the document.

So we replace entire "for" loop in the code with the following:

	for (var i = 0; i < filterValues.length; i++) {
		filterLinks.push("<a href="" + document.location.pathname + "?FilterField1=" + filterField + "&FilterValue1=" + filterValues[i] + "">" + filterValues[i] + "</a>");
	}
	document.write(filterLinks.join(filterValuesDelimiter));

By now the result should look something like the following:

Script so far.

Alphabet filter

And the first three lines of code give you configuration capabilities for filter field, filter values and filter values delimiter.

Neat. Want to complicate even more? Wouldn’t it be nice if the filter would highlight the selected value? And let’s make the highlighted style also configurable. At the beginning of the script first we need to declare a configuration variable for selected value.

	var selectedValueStyle = "font-weight: bold;";

Now we need to know which filter value is selected. How do we get to know that? In URL we need to read the value of the "FilterValue1" parameter. Since JavaScript has no built-in function for this we need a supporting queryString function. I’ve published one on my JavaScripts Library. It’s the qs function. We’ll insert this function before any JavaScript code, so immediately after the first line. But because we already use the iterator i in the for loop we need to fix the i iterator in the qs function.

	function qs(paramName) {
		var args = document.location.search.substring(1).split("&");
		for(j = 0; j < args.length; j++) {
			nameValues = args[j].split("=");
			if(nameValues[0] == paramName) return nameValues[1];
		}
		return null;
	}

And now we update the for loop in our script to be checking if the value of the FilterValue1 parameter equals the filter value from the array. We do this by injecting the shorthand if statement like the one below:

(qs("FilterValue1") == filterValues[i] ? "style="" + selectedValueStyle + "" " : "")

in the part where the links are being constructed (<a href="…"). The final for loop looks like this:

	for (var i = 0; i < filterValues.length; i++) {
		filterLinks.push("<a " + (qs("FilterValue1") == filterValues[i] ? "style="" + selectedValueStyle + "" " : "") + "href="" + document.location.pathname + "?FilterField1=" + filterField + "&FilterValue1=" + filterValues[i] + "">" + filterValues[i] + "</a>");
	}

And as a final touch let’s not have those links jammed to the upper left corner. We’ll wrap a layer around with a configurable style. So at the beginning of our script we add another parameter:

var filterDivStyle = "margin: 5px;";

and we update the "document.write" line to add the surrounding div.

document.write("<div style="" + filterDivStyle + "">" + filterLinks.join(filterValuesDelimiter) + "</div>");

And the final result looks like this (having applied filter for "A":

Final result with applied filter "A"

The full code is below:

<script type="text/javascript">
	function qs(paramName) {
		var args = document.location.search.substring(1).split("&");
		for(j = 0; j < args.length; j++) {
			nameValues = args[j].split("=");
			if(nameValues[0] == paramName) return nameValues[1];
		}
		return null;
	}
	
	var filterField = "FL";
	var filterValuesDelimiter = " : ";
	var filterValues = new Array("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z");
	var selectedValueStyle = "font-weight: bold;";
	var filterDivStyle = "margin: 5px;";
	var filterLinks = new Array();
	for (var i = 0; i < filterValues.length; i++) {
		filterLinks.push("<a " + (qs("FilterValue1") == filterValues[i] ? "style="" + selectedValueStyle + "" " : "") + "href="" + document.location.pathname + "?FilterField1=" + filterField + "&FilterValue1=" + filterValues[i] + "">" + filterValues[i] + "</a>");
	}
	document.write("<div style="" + filterDivStyle + "">" + filterLinks.join(filterValuesDelimiter) + "</div>");
</script>

And because results can be configurable, you can easily vary the look of the alphabet filter. For example a configuration like:

	var filterField = "FL";
	var filterValuesDelimiter = "-";
	var selectedValueStyle = "border: 1px black solid";
	var filterDivStyle = "margin: 5px; font-size: 15px;";

would result in

Variation in filter style

Happy scripting everyone!

Other articles from this series:

TOP