3. Quick Search in List View

Update: May 15th, 2010

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

Search is becoming more and more important. Even though SharePoint has pretty powerful search capability, I’m missing an inline quick search/filter for lists. And this is what we’ll do in this article.

A special note before we begin: This quick search is searching only between items in current view and current page, not in the whole list (for example if you have a view limited to 100 items and list contains more than 100 items, the quick search will “filter” only rows in current view.

Our goal and how we’ll do it

Our goal is as follows:

Type in here and list view gets filtered

Because the list view is a table, we’ll simply make a script that will look in each row of the list view and check if it contains the search string. If it does, it will set its display css property to table-row, otherwise it will set it to none. Let’s do this sample in a simple contacts list.

Let’s do it

After we insert content editor web part and link to the script as we can see below in the Hello world sample. (How to insert CEWP and manage code are described in earlier parts of this article series).

CEWP inserted Editing the source code of CEWP

Hello world works and now we’re ready to start writing our code. Don’t forget to delete the Hello world if you’ve also used it for testing :) .

Discovering the DOM object

First we need to discover the HTML DOM object that holds our rows to be filtered with search. As always IE Developer tools to the rescue. Fire up the IE Developer tools and using the HTML selector, click on any of the element in the List View rows.

Use HTML selector, Click anywhere in LVWP

Next in HTML DOM tree find the object’s parent table. When selected the table will be outlined in the page.

Selected object and parent table Highlighted parent table

Now let’s verify if this table has any useful ID, name or CSS class that is unique enough. In IE Developer toolbar switch to Attributes sub-tab:

Attributes sub-tab

The table has an ID defined by List GUID or View GUID ({D7C5F383-C0B5-4E6C-AB08-C27E1A65EC3B}-{31DFC207-90E8-4A43-8655-4CB90B30AC77}). That may be a bit too specific and we’d have to always modify this for other lists. We need something that is not bound to any List, web, item or site ID. It also has a CSS class ms-listviewtable. This looks promising. So in our source we insert the following code:

<script type="text/javascript">
function getElementsByCssClass(sTagName, sClassName) {
    var results = new Array();
    var allTagElements = document.getElementsByTagName(sTagName);
    for (i = 0; i < allTagElements.length; i++) {
        if(allTagElements[i].className == sClassName) results.push(allTagElements[i]);
    }
    return results;
}
</script>

Save and test the script and table. Reload the page and in browser’s address bar type:

javascript:alert(getElementsByCssClass(“table”,”ms-listviewtable”).length)

If you see an alert like the one below

Testing the getElementsByCssClass function

this means that the script is inserted, works ok and we have only one table with CSS class ms-listviewtable. Good start.

The Search function

Our search function will need to iterate through all rows (except header rows) of the table that we just got and check for content. If content contains our search string, it will show the row, if not it will hide it.

Our JavaScript function will take the term as an parameter for comparing to LV rows.

First let’s put our table in a variable for easier referencing. We’ll use the code from above to get the table by CSS class.

function quickSearch(term) {
	var lvTable = getElementsByCssClass("table","ms-listviewtable")[0];
}

Note in the second line above we have to add an index to result, because the getElementsByCssClass function returns an Array of objects.

Next we need to get an array of all table rows. We get those by traversing the DOM tree by using childNodes property which contains an array of all child elements.

	var lvRows = lvTable.childNodes[0].childNodes;

In the line above by using lvTable.childNodes[0] we got to TBODY element (even if you don’t have the tbody element in your HTML source, browser renders it). And the lvTable.childNodes[0].childNodes returns an array of all table rows.

To preview results of a function let’s add a temp alert here:

alert(lvRows.length);

Our quickSearch function until now looks as follows:

function quickSearch(term) {
	var lvTable = getElementsByCssClass("table","ms-listviewtable")[0];
	var lvRows = lvTable.childNodes[0].childNodes;
	alert(lvRows.length);
}

We can test it in the bowser, by refreshing URL and typing the following in the addressbar:

javascript:quickSearch()

Testing QuickSearch function

In the example above we now know that we have 101 table rows – 100 rows of a data view + 1 table headers.

Now it’s time to iterate through rows and compare their texts. The problem here arises in IE / FF compatibility. In IE you get the element’s text with innerText property. In FF you get it with textContent property. So for avoiding this issue we’ll prepare a small helper function:

function getTxt(obj) {
	if(obj.innerText) return obj.innerText;
	else return obj.textContent;
}

Now we update our quickSearch function. First delete the alert we’ve had for debugging and add a for loop for all rows but the first:

for(i = 1; i < lvRows.length; i++) {

}

and in that loop we’ll compare the search term to row’s inner text:

if(getTxt(lvRows[i]).toLowerCase().indexOf(term.toLowerCase()) > -1)

and in this condition we set the table row’s display property to “table-row”.

Our quickSearch function now looks as follows:

function quickSearch(term) {
	var lvTable = getElementsByCssClass("table","ms-listviewtable")[0];
	var lvRows = lvTable.childNodes[0].childNodes;
	for(i = 1; i < lvRows.length; i++) {
		if(getTxt(lvRows[i]).toLowerCase().indexOf(term.toLowerCase()) > -1) {
			lvRows[i].style.display = "table-row";
		}
		else lvRows[i].style.display = 'none';
	}
}

Adding a search box

Now that we have the function ready, we need a search box that will be calling this function.

Outside of JavaScript block in the source code add the following:

Quick Search: <input class="ms-long" type="text" onkeyup="quickSearch(this.value)" />

This will add a textbox with SharePoint’s native ms-long css style class (for styling purposes) and with an event after key has been pressed – to call the quick search function with its typed value.

Optimizing code a bit

Now the quick search is ready and can already be used. But if we look at the code, we can see that each time the key is pressed the quicksearch function will query the getElementsByCssName to find the table and the getElementsByCssName searches through entire DOM. We can optimize this code by putting lvRows variable ouside the function and populate it only once. This way we’ll have table rows always available and we won’t need to query them over and over again.

Our modified function looks as follows:

var lvRows = null;

function quickSearch(term) {
	if(lvRows == null) {
		var lvTable = getElementsByCssClass("table","ms-listviewtable")[0];
		lvRows = lvTable.childNodes[0].childNodes;
	}
...

To sum it all up:

As always if you don’t want to look at bits of the code through this article and just get the entire code, the entire code is below:

<script type="text/javascript">
function getElementsByCssClass(sTagName, sClassName) {
    var results = new Array();
    var allTagElements = document.getElementsByTagName(sTagName);
    for (i = 0; i < allTagElements.length; i++) {
        if(allTagElements[i].className == sClassName) results.push(allTagElements[i]);
    }
    return results;
}

function getTxt(obj) {
	if(obj.innerText) return obj.innerText;
	else return obj.textContent;
}

var lvRows = null;

function quickSearch(term) {
	if(lvRows == null) {
		var lvTable = getElementsByCssClass("table","ms-listviewtable")[0];
		lvRows = lvTable.childNodes[0].childNodes;
	}
	for(i = 1; i < lvRows.length; i++) {
		if(getTxt(lvRows[i]).toLowerCase().indexOf(term.toLowerCase()) > -1) {
			lvRows[i].style.display = "table-row";
		}
		else lvRows[i].style.display = 'none';
	}
}
</script>
Quick Search: <input class="ms-long" type="text" onkeyup="quickSearch(this.value)" />

Other articles from this series:

TOP