Click here to Skip to main content
15,904,023 members
Articles / Web Development / HTML
Article

HTML Table Filter ala Excel

Rate me:
Please Sign up or sign in to vote.
4.75/5 (37 votes)
3 Aug 2005CPOL1 min read 224.4K   2.3K   56   71
A way to filter a large table in a simple manner.

Demo

<script language="javascript"></script>

<input id="chk" önclick="enableFilter();" type="checkbox" name="chk" />Enable Filter

Col1Col2
1string
1string2
2String
2STRING2
3
1string
1string2
2String
2STRING2
3

Introduction

When I was writing a web application, I needed a way to filter a large table in a simple manner. The users of the application are used to the auto filtering in MS Excel, so I wanted my filter to have the same look and feel.

Features

  • Once a column is filtered, the dropdown boxes for the rest of the columns are adapted to the changes. Just like the auto filter in Excel works!
  • If a row has the class noFilter, the row will not be touched by the filter.

Using the code

Using the code is very simple. Just include the script to the page:

HTML
<script language="javascript" src="tablefilter.js"></script>

And activate the filter in the OnLoad:

HTML
<body onload="attachFilter(document.getElementById('tbl'), 1)">

attachFilter expects two parameters:

  1. The table object to attach the filter to.
  2. The row number at which the filter row should be inserted.

Browser Compatibility

The script was initially written for IE. I did test it successfully on Firefox, but I do not know if the script works for Netscape browsers.

History

  • 2005/07/27: First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralFilter returns the whole code plus value. Pin
chunchuyo8-Jul-10 5:26
chunchuyo8-Jul-10 5:26 
Generaltablefilter and paging Pin
braveheart0131-May-10 1:28
braveheart0131-May-10 1:28 
GeneralRe: tablefilter and paging Pin
WoutL31-May-10 1:51
WoutL31-May-10 1:51 
QuestionHello ! Pin
ehuddrey11-Nov-09 10:19
ehuddrey11-Nov-09 10:19 
AnswerRe: Hello ! Pin
WoutL11-Nov-09 20:47
WoutL11-Nov-09 20:47 
QuestionCan the dropdown filter be restricted to a single column? Pin
klsblues1-Jul-09 4:20
klsblues1-Jul-09 4:20 
AnswerRe: Can the dropdown filter be restricted to a single column? Pin
WoutL12-Jul-09 22:11
WoutL12-Jul-09 22:11 
GeneralRe: Can the dropdown filter be restricted to a single column? Pin
Member 1247297620-Apr-16 3:57
Member 1247297620-Apr-16 3:57 
QuestionHow to make the filtering work for the dropdown list column Pin
vijay_kumar16-Dec-08 0:32
vijay_kumar16-Dec-08 0:32 
AnswerRe: How to make the filtering work for the dropdown list column Pin
WoutL16-Dec-08 1:12
WoutL16-Dec-08 1:12 
Generalupdated JS for CUSTOM Filtering Pin
Dhruv P Patel7-May-08 5:56
Dhruv P Patel7-May-08 5:56 
<br />
// Attach the filter to a table. filterRow specifies the rownumber at which the filter should be inserted.<br />
<br />
// div elemnet where floating form for searching will get display<br />
var customDiv = "customDiv";<br />
// activityType for maintaining search activity<br />
var activityType = "";<br />
// keyword that need to be filter<br />
var keyword = "";<br />
// temp object that handle table object used when control is lost from <br />
// dropdown and called by floating form<br />
var tempTable;<br />
<br />
// used to maintain index of filterElements <br />
var tenmpIndex;<br />
<br />
// used to maintain FilterIndex  <br />
var tempFilterIndex;<br />
<br />
// used to maintain last index <br />
var isDisplayed= false;<br />
<br />
// add floating div<br />
document.write("<div id=""+customDiv+"" style="display:none;z-index:10;background:#E9EDD1;padding:4px;position:absolute;width:280px;border: 2px solid #D0D79F;"></div>");<br />
<br />
// cols ="all" means filter all column<br />
// cols ="0,2,4" column id 0 based index   <br />
function attachFilter(table, filterRow,cols)<br />
{<br />
	table.filterRow = filterRow;<br />
	table.filterCol = cols;<br />
	//get list of all columns<br />
	var filterColumns = cols.split(",");<br />
	<br />
	// Check if the table has any rows. If not, do nothing<br />
	if(table.rows.length > 0)<br />
	{<br />
		// Insert the filterrow and add cells whith drowdowns.<br />
		var filterRow = table.insertRow(table.filterRow);<br />
		var showFilter = false;<br />
		for(var i = 0; i < table.rows[table.filterRow + 1].cells.length; i++)<br />
		{<br />
			if(cols == "all")<br />
			{<br />
				//if show all columns  <br />
				showFilter = true;<br />
			}<br />
			else<br />
			{<br />
				//if selected then check if for correct column no<br />
				showFilter = false;<br />
				for(var index = 0; index < filterColumns.length &&  !showFilter ; index++)<br />
				{<br />
					if(i == filterColumns[index])<br />
						showFilter = true;<br />
				}<br />
			}<br />
			var c = document.createElement("TH");<br />
			table.rows[table.filterRow].appendChild(c);<br />
			// check if column need to be filtered<br />
			if(showFilter)<br />
			{<br />
				var opt = document.createElement("select");<br />
				opt.onchange = filter;<br />
				c.appendChild(opt);<br />
			}<br />
		}<br />
		// Set the functions<br />
		table.fillFilters = fillFilters;<br />
		table.inFilter = inFilter;<br />
		table.buildFilter = buildFilter;<br />
		table.showAll = showAll;<br />
		table.detachFilter = detachFilter;<br />
		table.filterElements = new Array();<br />
		<br />
		// Fill the filters<br />
		table.fillFilters();<br />
		table.filterEnabled = true;<br />
	}<br />
}<br />
<br />
<br />
function detachFilter()<br />
{<br />
	if(this.filterEnabled)<br />
	{<br />
		// Remove the filter<br />
		this.showAll();<br />
		this.deleteRow(this.filterRow);<br />
		this.filterEnabled = false;<br />
	}<br />
}<br />
<br />
// Checks if a column is filtered<br />
function inFilter(col)<br />
{<br />
	for(var i = 0; i < this.filterElements.length; i++)<br />
	{<br />
		if(this.filterElements[i].index == col)<br />
			return true;<br />
	}<br />
	return false;<br />
}<br />
<br />
// Fills the filters for columns which are not fiiltered<br />
function fillFilters()<br />
{<br />
	// check if request for all columns<br />
	if(this.filterCol == "all")<br />
	{<br />
		for(var col = 0; col < this.rows[this.filterRow].cells.length; col++)<br />
		{<br />
			// check if column is present or not<br />
			if(!this.inFilter(col))<br />
			{<br />
				// add column<br />
				this.buildFilter(col, "(all)");<br />
			}<br />
		}<br />
	}<br />
	else<br />
	{<br />
		// get list for selected columns<br />
		var cols = this.filterCol.split(",");<br />
		var col = 0;<br />
		for(var index = 0; index < cols.length; index++)<br />
		{<br />
			col = cols[index];<br />
			// check if column is present or not<br />
			if(!this.inFilter(col))<br />
			{<br />
				// add column<br />
				this.buildFilter(col, "(all)");<br />
			}<br />
		}<br />
	}<br />
}<br />
<br />
// Fills the columns dropdown box. <br />
// setValue is the value which the dropdownbox should have one filled. <br />
// If the value is not suplied, the first item is selected<br />
function buildFilter(col, setValue)<br />
{<br />
	// Get a reference to the dropdownbox.<br />
	var table;<br />
	// can be called on change in dropdown or by form<br />
	<br />
	if(this.rows != undefined)<br />
	{<br />
		//called from dropdown change<br />
		table = this;<br />
	}<br />
	else<br />
	{<br />
		//called from form so take tempTable<br />
		table = tempTable;<br />
	}	<br />
	<br />
	var opt = table.rows[table.filterRow].cells[col].firstChild;<br />
	<br />
	// remove all existing items<br />
	while(opt.length > 0)<br />
		opt.remove(0);<br />
	<br />
	var values = new Array();<br />
		<br />
	// put all relevant strings in the values array.<br />
	for(var i = table.filterRow + 1; i < table.rows.length; i++)<br />
	{<br />
		var row = table.rows[i];<br />
		if(row.style.display != "none" && row.className != "noFilter")<br />
		{<br />
			values.push(row.cells[col].innerHTML.toLowerCase());<br />
		}<br />
	}<br />
	values.sort();<br />
	<br />
	//add each unique string to the dopdownbox<br />
	var value;<br />
	for(var i = 0; i < values.length; i++)<br />
	{<br />
		if(values[i].toLowerCase() != value)<br />
		{<br />
			value = values[i].toLowerCase();<br />
			opt.options.add(new Option(values[i], value));<br />
		}<br />
	}<br />
<br />
	// add another option for selecting custom feature<br />
	opt.options.add(new Option("(custom)", "(custom)"), 0);<br />
	opt.options.add(new Option("(all)", "(all)"), 0);<br />
<br />
	if(setValue != undefined)<br />
		opt.value = setValue;<br />
	else<br />
		opt.options[0].selected = true;<br />
}<br />
<br />
// this function get caled when some one enter custom text and then say OK<br />
function setCustomValues()<br />
{<br />
	activityType = document.customForm.activityType.options[document.customForm.activityType.selectedIndex].value;<br />
	keyword = document.customForm.keyword.value;<br />
	activityType = trim(activityType);<br />
	// check for keyword<br />
	if(trim(keyword) == "")<br />
	{<br />
		// if keyword is blank then revert back<br />
		clearCustomValues()<br />
		return false;<br />
	}<br />
	// get meta Regex for keyword <br />
	keyword = getRegexMetaString(new String(keyword));<br />
	<br />
	if(activityType == "contains")<br />
	{<br />
		keyword = keyword;<br />
	}<br />
	else if(activityType == "equals")<br />
	{<br />
		keyword = "^"+keyword+"$";<br />
	}<br />
	else if(activityType == "startWith")<br />
	{<br />
		keyword = "^"+keyword;<br />
	}<br />
	else if(activityType == "endWith")<br />
	{<br />
		keyword = keyword+"$";<br />
	}<br />
<br />
	// check if tempIndex = -1 means some is selected custom and it is not present in filterElements list <br />
	if(tempIndex == -1)<br />
	{<br />
		// entry is not there so add before applying filter<br />
		var obj = new Object();<br />
		// it is actual pattern to match<br />
		obj.filter = keyword;<br />
		// previous is added to come back to lst valid state<br />
		obj.previous = keyword;<br />
		// assigning proper column index<br />
		obj.index = tempFilterIndex;<br />
		// wht is to be dislpay in dropdown<br />
		obj.display = "(custom)";<br />
		// add new obj to filterElements<br />
		tempTable.filterElements.push(obj);<br />
	}<br />
	else<br />
	{<br />
		// just change statistics of filterElements<br />
		tempTable.filterElements[tempIndex].display = "(custom)";<br />
		tempTable.filterElements[tempIndex].filter = keyword;<br />
		tempTable.filterElements[tempIndex].previous = keyword;<br />
	}<br />
	// apply all filters and display<br />
	showFilters(tempTable);<br />
	// reset all common variables<br />
	resetValues();<br />
	//hide the floating form<br />
	hideMatchingForm();<br />
	// return false to prevent page refreshing<br />
	return false;<br />
}<br />
<br />
//this function is called when some one doesnt want to apply custom filtering<br />
function clearCustomValues()<br />
{<br />
	// hide the floating form<br />
	hideMatchingForm();<br />
	<br />
	for(var i = 0; i < tempTable.filterElements.length; i++)<br />
	{<br />
		if(tempTable.filterElements[i].index == tempFilterIndex)<br />
		{<br />
			//alert(tempFilterIndex + " - "+tempTable.filterElements[i].filter+" - " +tempTable.filterElements[i].previous);<br />
			//set display to last valid state of dropdown box<br />
			tempTable.filterElements[i].filter = tempTable.filterElements[i].previous;<br />
		}<br />
	}<br />
	// apply all filters<br />
	showFilters(tempTable);<br />
	// reset all common variables<br />
	resetValues();<br />
}<br />
function resetValues()<br />
{<br />
	// resetting all values<br />
	activityType = "";<br />
	keyword= "";<br />
	tempTable = null;<br />
	tempIndex = -1;<br />
	tempFilterIndex = -1;<br />
	isDisplayed = false;<br />
}<br />
<br />
function trim(str)<br />
{<br />
	return str.replace(/(\s+$)|(^\s+)/g, '');<br />
	<br />
}<br />
// This function is called when a dropdown box changes<br />
function filter()<br />
{<br />
	// check if some one is selected custom but not using it<br />
	if(isDisplayed)<br />
	{<br />
		// if it is then reset last column by last valid state in drop down box<br />
		//alert("displayed "+tempFilterIndex);<br />
		// hide floating form<br />
		hideMatchingForm();<br />
		<br />
		for(var i = 0; i < tempTable.filterElements.length; i++)<br />
		{<br />
			if(tempTable.filterElements[i].index == tempFilterIndex)<br />
			{<br />
				//alert(tempFilterIndex + " - "+tempTable.filterElements[i].filter+" - " +tempTable.filterElements[i].previous);<br />
				//set display to last valid state of dropdown box<br />
				tempTable.filterElements[i].filter = tempTable.filterElements[i].previous;<br />
			}<br />
		}<br />
	}<br />
	<br />
	var table = this; // 'this' is a reference to the dropdownbox which changed<br />
	while(table.tagName.toUpperCase() != "TABLE")<br />
		table = table.parentNode;<br />
<br />
	var filterIndex = this.parentNode.cellIndex; // The column number of the column which should be filtered<br />
	var filterText = table.rows[table.filterRow].cells[filterIndex].firstChild.value;<br />
	var display = filterText;<br />
	// First check if the column is allready in the filter.<br />
	var bFound = false;<br />
	<br />
	for(var i = 0; i < table.filterElements.length; i++)<br />
	{<br />
		//alert(table.filterElements[i].index + " -" +table.filterElements[i].filter);<br />
		if(table.filterElements[i].index == filterIndex)<br />
		{<br />
			bFound = true;<br />
			// If the new value is '(all') this column is removed from the filter.<br />
			if(filterText == "(all)")<br />
			{<br />
				//remove from filterElements if all is selected <br />
				table.filterElements.splice(i, 1);<br />
			}<br />
			else if(filterText == "(custom)")<br />
			{<br />
				// if custom is selected then take backup of required variables<br />
				// reference cant be regenerate from form<br />
				// so assign to tempvariables<br />
				tempTable = table;<br />
				tempFilterIndex = filterIndex;<br />
				tenmpIndex = i;<br />
				//display form and return dont apply filter<br />
				displayMatchingForm();<br />
				return;<br />
			}<br />
			else<br />
			{<br />
				// if normal is there then just change entry details in filterElements <br />
				table.filterElements[i].display = filterText;<br />
				table.filterElements[i].filter = "^"+getRegexMetaString(new String(filterText))+"$";<br />
				table.filterElements[i].previous = table.filterElements[i].filter;<br />
			}<br />
			break;<br />
		}<br />
	}<br />
	if(!bFound)<br />
	{<br />
		// the column is added to the filter if no other selection is done <br />
		if(filterText == "(custom)")<br />
		{<br />
			//take backup and display form<br />
			tempTable = table;<br />
			tempFilterIndex = filterIndex;<br />
			tempIndex = -1;<br />
			<br />
			displayMatchingForm();<br />
			return;<br />
		}<br />
		//if not found and other then add in filterElements  <br />
		var obj = new Object();<br />
		obj.filter = "^"+getRegexMetaString(new String(filterText))+"$";<br />
		obj.previous = obj.filter;<br />
		obj.index = filterIndex;<br />
		obj.display = display;<br />
		table.filterElements.push(obj);<br />
	}<br />
	//finally apply filters<br />
	showFilters(table);<br />
	<br />
}<br />
<br />
function showFilters(table)<br />
{<br />
	// first set all rows to be displayed<br />
	table.showAll();<br />
	<br />
	// the filter ou the right rows.<br />
	for(var i = 0; i < table.filterElements.length; i++)<br />
	{<br />
		// First fill the dropdown box for this column<br />
		// reset dropdownbox with display name<br />
		table.buildFilter(table.filterElements[i].index, table.filterElements[i].display);<br />
		<br />
		// build regex from filterElements final filter pattern<br />
		var regEx = new RegExp(table.filterElements[i].filter);<br />
		// Apply the filter<br />
		for(var j = table.filterRow + 1; j < table.rows.length; j++)<br />
		{<br />
			var row = table.rows[j];<br />
			<br />
			if(table.style.display != "none" && row.className != "noFilter")<br />
			{<br />
				// if match is not found then hide<br />
				if(!row.cells[table.filterElements[i].index].innerHTML.toLowerCase().match(regEx))<br />
				{<br />
					row.style.display = "none";<br />
				}<br />
				/*<br />
				if(table.filterElements[i].filter != row.cells[table.filterElements[i].index].innerHTML.toLowerCase())<br />
				{<br />
					row.style.display = "none";<br />
				}<br />
				*/<br />
			}<br />
		}<br />
	}<br />
	// Fill the dropdownboxes for the remaining columns.<br />
	table.fillFilters();<br />
}<br />
<br />
function showAll()<br />
{<br />
	for(var i = this.filterRow + 1; i < this.rows.length; i++)<br />
	{<br />
		this.rows[i].style.display = "";<br />
	}<br />
}<br />
<br />
<br />
<br />
// display form<br />
function hideMatchingForm()<br />
{<br />
	document.getElementById(customDiv).style.display ='none';<br />
	isDisplayed = false;<br />
}<br />
<br />
// create form on runtime and display<br />
function displayMatchingForm()<br />
{<br />
	activityType ="";<br />
	isDisplayed= true;<br />
    var width = 300;<br />
    var height = 50;<br />
    var left = (screen.width - width) /2;<br />
    var top = (screen.height - height)/3;<br />
    var top = 10;<br />
	<br />
	<br />
	//document.getElementById(customDiv).style.width = width + 'px';<br />
    //document.getElementById(customDiv).style.height = height + 'px';<br />
    document.getElementById(customDiv).style.left = left + 'px';<br />
    document.getElementById(customDiv).style.top = top + 'px';<br />
	var addContent = <br />
		"<form name="customForm" onsubmit="return setCustomValues();">"+<br />
   			"<table width="40%">"+<br />
				"<tr>"+<br />
					"<td>"+<br />
						"<select name="activityType" style="width:100px">"+<br />
							"<option selected="" value="contains"> Contains</option>"+<br />
							"<option value="equals"> Equals</option>"+<br />
							"<option value="startWith"> Start With</option>"+<br />
							"<option value="endWith"> End With</option>"+<br />
						"</select>"+<br />
					"</td>"+<br />
					"<td>"+<br />
						"<input type="text" size="20" name="keyword">"+<br />
					"</input></td>"+<br />
				"</tr>"+<br />
				"<tr>"+<br />
					"<td>"+<br />
						"<input type="submit" style="width:100px" value="ok">"+<br />
					"</input></td>"+<br />
					"<td>"+<br />
						"<input type="button" style="width:100px" value="cancel" onclick="clearCustomValues();">"+<br />
					"</input></td>"+<br />
				"</tr>"+<br />
			"</table>"+<br />
		"</form>";<br />
	<br />
    // add to your div an header	<br />
	document.getElementById(customDiv).innerHTML = addContent;<br />
	//alert(document.getElementById(customDiv).innerHTML);<br />
	document.getElementById(customDiv).style.display ='';<br />
}<br />
<br />
// escape regex meta characters<br />
function getRegexMetaString(input)<br />
{<br />
		input = input.replace("\\","\\");<br />
		input = input.replace(".","\\.");<br />
		input = input.replace("|","\\|");<br />
		input = input.replace("(","\\(");<br />
		input = input.replace(")","\\)");<br />
		input = input.replace("{","\\{");<br />
		input = input.replace("}","\\}");<br />
		input = input.replace("[","\\[");<br />
		input = input.replace("]","\\]");<br />
		input = input.replace("*","\\*");<br />
		input = input.replace("+","\\+");<br />
		input = input.replace("?","\\?");<br />
		input = input.replace("^","\\^");<br />
		input = input.replace("$","\\$");<br />
<br />
		/*<br />
		input = input.replace("\\","\\\\");<br />
		input = input.replace(".","\\\\.");<br />
		input = input.replace("|","\\\\|");<br />
		input = input.replace("(","\\\\(");<br />
		input = input.replace(")","\\\\)");<br />
		input = input.replace("{","\\\\{");<br />
		input = input.replace("}","\\\\}");<br />
		input = input.replace("[","\\\\[");<br />
		input = input.replace("]","\\\\]");<br />
		input = input.replace("*","\\\\*");<br />
		input = input.replace("+","\\\\+");<br />
		input = input.replace("?","\\\\?");<br />
		input = input.replace("^","\\\\^");<br />
		input = input.replace("$","\\\\$");<br />
		*/<br />
		return input;<br />
}<br />
<br />

GeneralRe: updated JS for CUSTOM Filtering (More Info) Pin
Dhruv P Patel7-May-08 5:59
Dhruv P Patel7-May-08 5:59 
Generalupdated JS for table filtering with CUSTOM Pin
Dhruv P Patel7-May-08 5:53
Dhruv P Patel7-May-08 5:53 
Generalnew feature Pin
Dhruv P Patel6-May-08 21:05
Dhruv P Patel6-May-08 21:05 
GeneralRe: new feature Pin
WoutL6-May-08 21:20
WoutL6-May-08 21:20 
QuestionNewbie Help Pin
hgee5-Nov-07 8:54
hgee5-Nov-07 8:54 
AnswerRe: Newbie Help Pin
WoutL5-Nov-07 23:18
WoutL5-Nov-07 23:18 
Generalnice code, but could it do this... :) Pin
andrewmeda6231-Aug-07 11:33
andrewmeda6231-Aug-07 11:33 
GeneralRe: nice code, but could it do this... :) Pin
WoutL1-Aug-07 20:12
WoutL1-Aug-07 20:12 
GeneralStill Working Great. Pin
belesland19-Jul-07 2:34
belesland19-Jul-07 2:34 
QuestionTable size not changing? Pin
arniedows16-May-07 20:46
arniedows16-May-07 20:46 
AnswerRe: Table size not changing? Pin
WoutL16-May-07 21:56
WoutL16-May-07 21:56 
Questionhyperlinks Pin
arizen15-Jan-07 1:14
arizen15-Jan-07 1:14 
AnswerRe: hyperlinks Pin
WoutL15-Jan-07 1:26
WoutL15-Jan-07 1:26 
QuestionRe: hyperlinks Pin
arizen15-Jan-07 1:37
arizen15-Jan-07 1:37 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.