当我们的页面中数据表行非常多时,为了很方便的找到我们需要的及相关纪录行时,大都采用排序的方式,对于现在的开发,几乎不需要写什么代码就能很容易的排序,如:asp.net 2.0中的GridView,如果数据源是DataSet的话,什么都不用写,仅需要设置几个属性就可以了。但这种排序存在着性能上劣势,因为你每次排序时,都会提交该页面,在服务器端进行相应的排序操作,然后将生成的排序页面传递到客户端,这样作会增加用户的等待时间,影响用户的使用感觉。
如果用户想对他所查看的客户端数据进行一个快速的排序,而不希望将页面提交到服务器,花费太长的等待时间,这时候我们就应该采用客户端排序的解决方案。
仔细的研究一下数据表中的数据,我们就可以发现其中的各项数据可以分为这样三种类型:字符串、时间、数字。客户端的排序实质上就是实现这三种数据类型的排序。
在javaScript中虽然提供了Date的数据类型,但为了获得更好的性能,减少由字符串向Date类型转换的成本,我们在这里采用了服务端数据输出妥协的办法,指的是在服务器端输出时间类型的字段时,可以利用string.Format("{0:yyyy-MM-dd}", DateValue)这种方式来生成适合客户端排序的时间字符串。这样时间的排序其实就转化为了字符串的排序。
到这里关于排序的数据类型已经确定了下来,接下来的就是如何实现了。
在排序中要用到如下的几个关键的技术点:
1. currentTable.rows[i].cells[j].innerText.toLowerCase()
2. 冒泡排序
3. currentTable.moveRow(i,i+1);
4. 利用eval(numberString)或者new Number(numberString)实现字符串向数字的转换;
剩下的基本上属于代码开发技巧上的东西了。
在实际的应用中,为了避免用户在查看每一行数据不至于和其他行相互混淆,我们常设置表格隔行背景颜色,这还不够,要更好的改善用户体验,还要利用数据行上鼠标的onmouseover,onmouseout及onmouseclick事件来改变当前行的背景色。这样用户就可以更加清楚地查看每一行的数据了。
本节所叙述的解决方案是做成了一个*.htc,目的是为了以最方便的方式使用该程序,使用起来非常简单,仅需要设置当前table的style属性就可以了。您可以用如下方式实现数据表客户端排序的功能:
<table style=”behavior:url(lovetable.htc)” overColor=”red” clickColor=”Yellow”>……
就这么简单,套用了该样式的表将会自动启用各列的排序功能。其中的overColor、clickColor分别为鼠标滑过、单击时对应行的背景色;你也可以不设,而采用htc提供的默认颜色。此htc是针对asp.net 2.0 中的gridview 量身定做,你可以用如下方式利用到gridview上:<asp:GridView runat="server" BackColor="#E5F1FF" BorderColor="#E5F1FF" BorderWidth="0px" CellPadding="3" CellSpacing="1" GridLines="None" Style="behavior: url(App_Themes/Blue/loveTable.htc)">.....同样可以在其中设定overcolor和clickcolor属性的值。
本文代码如下:
loveTable.htc(V1.0)
<PUBLIC:ATTACH EVENT="oncontentready" ONEVENT="init()" />
<PUBLIC:PROPERTY NAME="OverColor" />
<PUBLIC:PROPERTY NAME="ClickColor" />
<SCRIPT LANGUAGE="JScript">
/* loveTable.htc
table client sort,table row mouse move/click events response.
Author: Ge.shaofei(2005.8.23 --- 2005.8.25)
Description:
A. realize data table client every column up and down sorting.
B. using *.htc to provider the easiest way to utilize it.
C. response the onmouseover,onmouseout,onclick event to change the current row backgroundcolor.
D. To be modify to adapter advanced requirement.
Design Roadmap: A: bubble sorting;
B: currentTable.rows[i].cells[j].innerText.toLowerCase();
C: currentTable.moveRow(i,i+1);
D: use eval(numberString) or new Number(numberString) to convert numberString to number;
Version Specification: V1.0 ;
Use Method: <table style="behavior:url(lovetable.htc)" overColor="red" clickColor="Yellow">.....</table>
Use Attention: A. the browser must support *.htc;
B. overColor/clickColor can be ignored to use the default values;
C. change the sorting image to the right path;
*/
var lastclick = -1;
var reverse = false;
var clickObject;
var arrIsNumberCols;
var bc1,bc2;
var validRowsCount;
var overColor,clickColor;
function init()
{
if(element.rows.length < 2) return;
element.attachEvent("onmouseover",overthis);
element.attachEvent("onmouseout",outthis);
element.attachEvent("onclick",clickthis);
if(OverColor == null) OverColor = "#B4D7FF";
if(ClickColor == null) ClickColor = "#ffefce";
theadrow = element.rows[0];
theadrow.runtimeStyle.cursor = "hand";
validRowsCount = element.rows.length;
if(element.rows[validRowsCount-1].cells.length == 1)
validRowsCount = validRowsCount-1;
colCount = theadrow.cells.length;
arrIsNumberCols = new Array(colCount);
var l, clickCell;
for (var i=0; i<colCount; i++)
{
l=document.createElement("IMG");
l.src="App_Themes/Blue/images/blank.gif";
l.id="srtImg";
l.width=8;
l.height=7;
clickCell = theadrow.cells[i];
clickCell.selectIndex = i;
clickCell.insertAdjacentElement("beforeEnd", l)
clickCell.attachEvent("onclick", doClick);
arrIsNumberCols[i] = isNumberCol(i);
}
bc1 = element.rows[1].style.backgroundColor;
if(validRowsCount > 1)
{
for( i=2;i <= validRowsCount-1; i++)
{
if(element.rows[i].style.backgroundColor != bc1)
{
bc2 = element.rows[i].style.backgroundColor;
break;
}
}
}
if( bc2 == null ) bc2 = bc1;
}
function isNumberCol(colIndex)
{
var numberCol = true;
for( i = 1; i < validRowsCount; i++ )
{
if(isNaN(element.rows[i].cells[colIndex].innerText))
{
numberCol = false;
break;
}
}
return numberCol;
}
function doClick(e)
{
clickObject = e.srcElement;
if(clickObject.tagName == "A") return;
while (!((clickObject.tagName == "TH") || (clickObject.tagName == "TD")))
{
clickObject = clickObject.parentElement;
}
if(lastclick == clickObject.selectIndex)
{
if(reverse == false)
{
clickObject.children['srtImg'].src = "App_Themes/Blue/images/down.gif";
reverse = true;
}
else
{
clickObject.children['srtImg'].src = "App_Themes/Blue/images/up.gif";
reverse = false;
}
}
else
{
reverse = false;
if(lastclick != -1) theadrow.all('srtImg')[lastclick].src = "App_Themes/Blue/images/blank.gif";
lastclick = clickObject.selectIndex;
clickObject.children['srtImg'].src = "App_Themes/Blue/images/up.gif";
}
insertionSort();
hilite();
}
function insertionSort()
{
var comparedText,currentText,tempRow;
var colIndex = clickObject.selectIndex;
var exchange;
for ( i = 1 ; i < validRowsCount ; i++ )
{
exchange = false;
for ( j = validRowsCount-2; j >= i ; j-- )
{
comparedText = element.rows[j].cells[colIndex].innerText.toLowerCase();
currentText = element.rows[j+1].cells[colIndex].innerText.toLowerCase();
if (arrIsNumberCols[colIndex])
{
comparedText= eval(comparedText);
currentText= eval(currentText);
}
if ((reverse && currentText > comparedText) || (!reverse && currentText < comparedText))
{
element.moveRow(j,j+1);
exchange = true;
}
}
if(!exchange)
break;
}
}
function hilite()
{
var hiliteSwitch = false;
var getClickbakRowIndex = false;
for(i=1;i < validRowsCount; i++)
{
if(hiliteSwitch)
{
element.rows[i].style.backgroundColor = bc1;
}
else
{
element.rows[i].style.backgroundColor = bc2;
}
hiliteSwitch = !hiliteSwitch;
if(!getClickbakRowIndex)
{
if(element.rows[i].runtimeStyle.backgroundColor == ClickColor)
{
clickbakRowIndex = i;
getClickbakRowIndex = true;
}
}
}
}
function overthis()
{
if(event.srcElement != element)
{
var currentRow = getTR();
if(currentRow.rowIndex != 0 && currentRow.rowIndex != clickbakRowIndex)
{
currentRow.runtimeStyle.backgroundColor = OverColor;
}
}
}
function outthis()
{
if(event.srcElement != element)
{
var currentRow = getTR();
if(currentRow.rowIndex != 0 && currentRow.rowIndex != clickbakRowIndex)
currentRow.runtimeStyle.backgroundColor = '';
}
}
var clickbakRowIndex = -1;
function clickthis()
{
if(event.srcElement != element)
{
var currentRow = getTR();
if(currentRow.rowIndex != 0)
{
if(clickbakRowIndex != -1)
{
element.rows[clickbakRowIndex].runtimeStyle.backgroundColor = ''
}
clickbakRowIndex = currentRow.rowIndex;
currentRow.runtimeStyle.backgroundColor = ClickColor;
}
}
}
function getTR()
{
var obj = event.srcElement.parentElement;
while (obj.tagName != "TR")
obj = obj.parentElement;
return obj;
}
</SCRIPT>