First Name | Middle Name | Last Name | Age |
Robert | James | Anderson | 29 |
Henry | Jonathan | Murray | 40 |
Edward | Richard | Jones | 32 |
Patrick | Elroy | Emerson | 53 |
Lisa | Anne | Smith | 49 |
Table Sorter
The main focus of this experiment was to see what could be done to a raw HTML table that has not been written with the functionality you see in mind, I.E -- no ID's to reference
table rows and columns, no hard coded onclick events, etc. As you can see from the source, none of these things exist for the above table -- it is nothing more than bare bones HTML.
Onclick events for the TDs and TRs are provided programatically as the code loops over the length of those elements. Determining what row and column were clicked on is done with the offset properties of the objects and the clientX/pageX, clientY/pageY of the mouse captured from the documents onmousedown event. Once the column or row has been determined, its source index is passed to the appropriate functions which perform the manipulations on the objects.
The rows are sorted when one of the heading columns is clicked. First, the column is determined, which gives us our source index for the TD objects within their parent TR objects. From there, we add their innerHTML and their original source index to a multi-dimensional array, sort it using the sort() method, then using that data we clone them using cloneNode, adding that new object to an array. The code then uses insertBefore to insert the cloned objects into the DOM, in order, and the original objects are removed using removeChild.
Using this method of manipulation leaves the process completely open ended. Any table of data will be sortable with this method, regardless of the number of columns or rows.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>slayeroffice | table sorter</title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-15" /> <style type="text/css"> body { margin:10px; } table { font-family:verdana; font-size:9pt; border:1px solid #000000; cursor:default; width:300px; color:#FFFFFF; } td { padding:5px; } </style> <script language="javascript"> if(!document.all)document.captureEvents(Event.MOUSEDOWN); var rowStart = 1; // excludes the heading row from manipulations. change to 0 if there is no heading. var moz_firstSort = true; var col_evenColor = "#006699"; var col_oddColor = "#00968C"; var col_highlightColor = "#008299"; function init() { initRows(); if(rowStart)initHeading(); } function initHeading() { for(i=0;i<document.getElementsByTagName("tr")[0].childNodes.length;i++) { if(document.getElementsByTagName("tr")[0].childNodes[i].tagName == "TD") { document.getElementsByTagName("tr")[0].childNodes[i].onclick = initSort; document.getElementsByTagName("tr")[0].childNodes[i].style.backgroundColor = "#FFFFFF"; document.getElementsByTagName("tr")[0].childNodes[i].style.color = "#000000"; document.getElementsByTagName("tr")[0].childNodes[i].style.cursor = "pointer"; } } } function initSort(e) { mouseX = document.all?window.event.clientX-11:e.pageX - 11; column = returnColumnClicked(mouseX); handleSort(column); } function initRows() { colorizeTableRows(); for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) { document.getElementsByTagName("tr")[i].id = "row" + i; document.getElementsByTagName("tr")[i].onclick = handleRowClick; } } function colorizeTableRows() { for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) { i%2?document.getElementsByTagName("tr")[i].style.backgroundColor = col_evenColor:document.getElementsByTagName("tr")[i].style.backgroundColor = col_oddColor; } } function colorizeTableColumn(column) { for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) { for(z=0;z<document.getElementsByTagName("tr")[i].childNodes.length;z++) { if(document.getElementsByTagName("tr")[i].childNodes[z].tagName == "TD")document.getElementsByTagName("tr")[i].childNodes[z].style.backgroundColor = ""; } document.getElementsByTagName("tr")[i].childNodes[column].style.backgroundColor = col_highlightColor; } } function handleRowClick(e) { mouseY = document.all?window.event.clientY-11:e.pageY-11; row = returnRowClicked(mouseY); highlightTableRow(row); } function highlightTableRow(row) { colorizeTableRows(); document.getElementsByTagName("tr")[row].style.backgroundColor = col_highlightColor; } function handleSort(column) { sortBy = new Array(); dataID = document.all?column[1]:column[0]; for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) { sortBy[sortBy.length] = document.getElementsByTagName("tr")[i].childNodes[dataID].innerHTML; } if(rowStart) { resetHeaderColors(); document.getElementsByTagName("tr")[0].childNodes[dataID].style.backgroundColor = col_highlightColor; } sortRowData(dataID); } function resetHeaderColors() { for(i=0;i<document.getElementsByTagName("tr")[0].childNodes.length;i++) { if(document.getElementsByTagName("tr")[0].childNodes[i].tagName == "TD")document.getElementsByTagName("tr")[0].childNodes[i].style.backgroundColor = "#FFFFFF"; } } function sortRowData(dataID) { order = new Array(); colorizeTableColumn(dataID); for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) order[order.length] = new Array(document.getElementsByTagName("tr")[i].childNodes[dataID].innerHTML,i); order=order.sort(); reorderTable(order); } function reorderTable(order) { mDiv = new Array(); // create sorted object references for(i=0;i<order.length;i++) mDiv[mDiv.length] = document.getElementsByTagName("tr")[order[i][1]].cloneNode(true); // insert sorted TR objects z=0; for(i=rowStart;i<document.getElementsByTagName("tbody")[0].childNodes.length;i++) { if(document.getElementsByTagName("tbody")[0].childNodes[i].tagName == "TR") { try { if(document.all) { document.getElementsByTagName("tbody")[0].insertBefore(mDiv[z],document.getElementsByTagName("tr")[i]); } else { document.getElementsByTagName("tbody")[0].insertBefore(mDiv[z],document.getElementsByTagName("tr")[document.getElementsByTagName("tr").length]); } } catch(err) { } z++; } } removeChildren(mDiv.length+1); initRows(); } function removeChildren(startFrom) { err =""; if(document.all) { do { try { document.getElementsByTagName("tbody")[0].removeChild(document.getElementsByTagName("tr")[startFrom]) } catch (err) { } } while (err == ""); } else { z=0; for(i=1;z<startFrom-1;i++) { if(document.getElementsByTagName("tbody")[0].childNodes[i].tagName == "TR") { if(moz_firstSort) { document.getElementsByTagName("tbody")[0].removeChild(document.getElementsByTagName("tbody")[0].childNodes[i]); } else { document.getElementsByTagName("tbody")[0].removeChild(document.getElementsByTagName("tbody")[0].childNodes[startFrom+1]); } z++; } } } if(moz_firstSort)moz_firstSort = false; // mozilla sure does suck sometimes... } function returnRowClicked(y) { for(i=rowStart;i<document.getElementsByTagName("tr").length;i++) { height = document.getElementsByTagName("tr")[i].offsetHeight; if(y>=document.getElementsByTagName("tr")[i].offsetTop && y<=document.getElementsByTagName("tr")[i].offsetTop + height)return i; } } function returnColumnClicked(x) { tdIncr = 0; // mozilla counts text nodes as child nodes, ie tabbed code for legibility, so returning "i" will result in incorrect TD indexes for(i=0;i<document.getElementsByTagName("tr")[0].childNodes.length;i++) { if(document.getElementsByTagName("tr")[0].childNodes[i].tagName == "TD") { width = document.getElementsByTagName("td")[i].offsetWidth; if(x>=document.getElementsByTagName("tr")[0].childNodes[i].offsetLeft && x<= document.getElementsByTagName("tr")[0].childNodes[i].offsetLeft + width) { columnInfo = new Array(i,tdIncr); return columnInfo; } tdIncr++; } } } </script> <body onload="init();"> <table cellpadding="0" cellspacing="0"> <tr><td>First Name</td><td>Middle Name</td><td>Last Name</td><td>Age</td></tr> <tr><td>Robert</td><td>James</td><td>Anderson</td><td>29</td></tr> <tr><td>Henry</td><td>Jonathan</td><td>Murray</td><td>40</td></tr> <tr><td>Edward</td><td>Richard</td><td>Jones</td><td>32</td></tr> <tr><td>Patrick</td><td>Elroy</td><td>Emerson</td><td>53</td></tr> <tr><td>Lisa</td><td>Anne</td><td>Smith</td><td>49</td></tr> </table> </body> </html>