3. Quick Search in List View
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:
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).
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.
Next in HTML DOM tree find the object’s parent table. When selected the table will be outlined in the page.
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:
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:
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
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()
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:
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:
Quick Search:
Other articles from this series:
[…] 3. Quick Search in List View […]
Ran across this and thought it was a great solution. However, I have run across issues when a user is using IE6 or IE7. For some reason they receive the dreaded “Could not get the display property.” error. I will admit that I am not as proficient with JS as I should and therefore thought it had to do with the double quotes used on line 25. No dice though. When I test using IE8, it works like a charm.
I also had to modify the getElementsByCssClass function, replacing line 6 with the code below to allow for multiple class names — class=”aClass bClass”.
var className=allTagElements[i].className;
if(className.match(sClassName)) results.push(allTagElements[i]);
Any ideas what could be going on?
Hello, MJM. Thanks for sharing this. It’s true. IE7 and prior don’t know how to handle the display property if it isn’t explicitly specified as an element attribute. I’ll have to modify the script a bit. Also thanks for pointing out the getelementsbycssclass method. True it’s matching only if Css class is exactly the same. I’ll modify this also.
Hello Boris.
I’m a beginner in SharePoint and in JS as well. I found your script very interesting but unfortunately I use IE7 and when I implemented your script I got JS errors. I assume that is the same thing as MGM is talking about. Can you give some hint how to fix this script that will work for IE7 ?
Thank you very much. I learned a lot of things from your site!
Best Regards.
To get it working in ie6 I had to change any
…style.display = “table-row”;
to
…style.display = “block”;
This is just a wonderful tutorial. I’ve modified it to only filter on enter, added a clear search results button and allowed it to search for wildcard style for multiple terms separated by spaces.
I am wondering, however, if there is a way to have this functionality on all the rows, including the ones not in view, or on metadata that is not in view. I don’t suppose there is anyway you know of doing this, hm?
Hello, Krit! I was wondering, since you manipulated the code so well, is it possible to make the filter point to a specific webpart? I am asking because I have a page with many wbeparts, and need teh search to filter just one webpart.
Thanks!
Rudy
Can something like this ever work with dataview or does it require direct list view?
If you’re working with SharePoint Designer’s Data View Webpart you can. It’s even easier, because you can completely customize table, rows’ ids and properties.
: Yes, I was using the style.display = “block” too. But browsers like FireFox or Chrome don’t like this display property for table rows. If you’re using IE, then there is no problem. Otherwise yes, you can make this on all rows. You’d have to get all rows in document. But the script gets a bit more complex here because if you hide a parent table row all the child table rows get hidden.
: The problem in IE7 is that it’s unable to get the “display” property of an HTML element if it doesn’t have it explicitly declared. A better alternative is to use CSS classes to ensure it would work also in IE7. For the fastest I’d recomend updating to IE8, but I’ll also update this article to be working with CSS classes instead of directly display properties.
What a super tool!!!! And so simple to deploy!
Boris, thank you very much for that thing!!!!!
Excellent tool – it worked straight out of the box.
Thanks
Iain
Great script and easy to follow for us “non-coders”. Can you show me how to narrow down my search to only look in say, the “user” column? Thanks.
Hey, we “non-coders” aren’t so dumb after all!
var lvTable = getElementsByCssClass(“table”,”ms-listviewtable”,”user”)[0];
searched only the “user” column.
thanks again.
how can I use this for link list. I created Link list for team sites( 123 team sites) and add to page (web part) I want to use this quick search feature to search that list.
The above code dosen’t work for link list. Appreciate any solution for above issue.
Thanks
venu
Hi I’m using internet explorer 8 and i’m not able get any results after i hit enter after putting my strings
please help
thanks
Great script, works as advertised! That’s a rare find. I do have a question though…if you have more than one table on a SP page, where in your script would you plug in the web part ID? I’ve been trying various approaches and it’s eluded me so far.
Thanks a million in advance,
Dawn
The best way would be to loop through tables of webparts holding listviews. and running filter codes.
Boris, because you did the due diligence of axplaining the process out so well (like using explorer tools to look for the table ID) I was able to take this code and create a filter for an older version of SharePOint. I am a teach, nto a coder, so this is that much more a testament to your article.
Thank you SO MUCH for your time, and how do I donate to your site?
Rudy
Thank you for your kind words.
Boris.
I would like to use this view to filter 1 webpart list in a page that has 5 or 6 other wbepart lists. Is it possible to use a table-specific designator (like GUID) to enable this filter to this?
Thanks!
Nice code you got there, unfortunately….
1) This code wont work with grouped list.
Is there any workaround on how to settle this one?
2) You won’t get the same searched result if you move to next result page. Let say you got limit 50 rows per-view & the result return you with 3 page of result (150 rows), the 2nd(51-100 rows) & 3rd(101-150 rows) result page wont display the correct result as it should.
Awesome code, Boris.
Is it possible to search across multiple lists?
I have several list webparts in a single page. It seems to only search the first list.
The code gets much more complicated. The way I’d do it is to discover all the tables of list views in the pages, and run that filter on all of them.
Hi XicanoWan,
Did you figure it out? I am having the same scenario here. I have 4 list webparts in my page and need a way to search for all at once.
Thanks!
I read the last posts. I managed to code a solution to the problem of IE7 and others browsers, in a post is pointed that:
Change any
…style.display = “table-row”;
to
…style.display = “block”
Thank Krit
But that only works to IE6 or 7 not in the others to do this works in the mayority of browsers, is necesary write the following lines:
if (navigator.appName==”Microsoft Internet Explorer”)
lvRows[i].style.display = “block”;
else
lvRows[i].style.display = “table-row”;
and ready
Thanks
Thank you very much for the code! works great even after 2 years 😀
Dafna
Has anyone found a way to get this to work on grouped lists?
Thanks.
thanks………..
Hi. Great work. This works like a charm. However I really need it for one of my grouped lists. Can anybody please help?
Hi – this is awesome. I would like to narrow down the search to a column and be able to do a multiple values search. Would you have a good suggestion for that?
Hi – THANK YOU A BILLIZON TIMES FOR THIS!! I was able to get it to work by changing the “style.display”=table-row to block; which even works in firefox, surprisingly.
I have been trying to modify this to work for DVWP; or a list view in preview pane. The plain list view doesn’t work for our customers. I’ve attempted to change the class element type to no avail.
Ideally it would work like this
row 1: Title Author Date Approval status
row 2: detailed content Header
row 3: detailed content
and the search would work on the grouping of all three rows so that when you search for author it still shows the information in rows 2 & 3; or if you search for a word in the detailed content it shows rows 1 & 2.
Any suggestions?
Thank you again for this great simple code