Since a lot of the implementaion follows the old version I'm going to try to concentrate on the more important parts. Espescially how the sorting is done and how this was done to maximize performance.
Just like in the old version the Array
sort
is
used, but unlike the old version most of the work that was done in the compare
function has been moved out of this function and is done once before the
sorting starts.
Before the sorting is started an array is made that contains JavaScript objects which contains the needed information to do the actual sorting of the table. This information consists of the value used to sort the rows as well as a reference to the actual row element so that we can reorder the rows once the sorting is done.
SortableTable.prototype.getCache = function (sType, nColumn) { var rows = this.tBody.rows; var l = rows.length; var a = new Array(l); var r; for (var i = 0; i < l; i++) { r = rows[i]; a[i] = { value: this.getRowValue(r, sType, nColumn), element: r }; }; return a; };
One thing to note here is that the length
is calculated once
before the loop. The reason for this is that getting rows.length
is an expensive operation in IE and we do not want to do it once for every
row.
Once we have this array we do not need to interact with the DOM during the actual sorting of the array.
The function getCache
prepares the data and once that is done
the actual code to sort the table is more or less book keeping. The compare
function to use when sorting is returned by the method
getSortFunction
. In its current state this function just returns
a funtion that compares the value
properties of the object inside
the cache array. One might want to modify this for more esoteric data types
where the operator <
is not defined.
Once the cache array has been sorted the table rows are reinserted in the
same order as the cache array. The property element
of the JS
object in the array is used to find the correct row element.
SortableTable.prototype.sort = function (nColumn, bDescending, sSortType) { if (sSortType == null) sSortType = this.getSortType(nColumn); // exit if None if (sSortType == "None") return; if (bDescending == null) { if (this.sortColumn != nColumn) this.descending = true; else this.descending = !this.descending; } this.sortColumn = nColumn; if (typeof this.onbeforesort == "function") this.onbeforesort(); var f = this.getSortFunction(sSortType, nColumn); var a = this.getCache(sSortType, nColumn); var tBody = this.tBody; a.sort(f); if (this.descending) a.reverse(); if (SortableTable.removeBeforeSort) { // remove from doc var nextSibling = tBody.nextSibling; var p = tBody.parentNode; p.removeChild(tBody); } // insert in the new order var l = a.length; for (var i = 0; i < l; i++) tBody.appendChild(a[i].element); if (SortableTable.removeBeforeSort) { // insert into doc p.insertBefore(tBody, nextSibling); } this.updateHeaderArrows(); this.destroyCache(a); if (typeof this.onsort == "function") this.onsort(); };
In some browser (namely Mozilla) the DOM manipulations are faster when
done on a disconnected DOM tree. Therefore when
SortableTable.removeBeforeSort
is true we first disconnect the
tree before reorganizing the rows and once the reorganization is done we
reinsert the table.
When the SortableTable
is constructed images are added to
the table header cells. These images then changes their background image
to indicate that the column is sorted.
Also at creation, event listeners are added to the table header cells. In
this case we use addEventListener
if available and if not we
try to use attachEvent
. These are set to call
this._headerOnclick
, which is just an encapsulation of
this.headerOnclick()
. There are 2 reasons why it is done in this
way. The first is so that this
will point to the JS object inside
the function headerOnclick
and not on the table cell element. The
other reason is that we need to be able to detach the event listener when
destroying the JS object. If we used an anonymous function this would not
have been possible.
function SortableTable(oTable, oSortTypes) { ... var oThis = this; this._headerOnclick = function (e) { oThis.headerOnclick(e); }; ... } SortableTable.prototype.initHeader = function (oSortTypes) { var cells = this.tHead.rows[0].cells; var l = cells.length; var img, c; for (var i = 0; i < l; i++) { c = cells[i]; img = this.document.createElement("IMG"); img.src = "images/blank.png"; c.appendChild(img); if (oSortTypes[i] != null) { c._sortType = oSortTypes[i]; } if ("addEventListener" in c) c.addEventListener("click", this._headerOnclick, false); else if ("attachEvent" in c) c.attachEvent("onclick", this._headerOnclick); } this.updateHeaderArrows(); };