template
Index :: Javascript :: Module :: Tabelle sortieren (
tblsort.js)
Dieses Modul kapselt das Sortieren einer HTML-Tabelle. Unterstützt werden hierbei diverse Spaltentypen (Formate von Zelleninhalten) sowie aufsteigende/absteigende Reihenfolge. Siehe auch die
Demo der Klasse.
Jede Tabelle besitzt implizit einen TBODY-Kindknoten, welcher seinerseits alle TR-Knoten enthält. Er wird beim Initialisierungsvorgang durch tabelle.firstChild ermittelt und in einer Variablen gespeichert. So können später in einer Schleife alle TR-Kindknoten angesprochen werden.
Zur Sortierung selbst wird die sort-Methode des Array-Objekts verwendet. Zunächst muss also jeweils ein entsprechendes Hilfs-Array vorbereitet werden. Die Schritte im einzelnen:
- In einer ersten Schleife werden alle TBODY-Kindknoten durchlaufen. Trifft man auf einen TR-Knoten, dann wird sein N-ter TD-Kindknoten ermittelt (N = Position der zu sortierenden Spalte). Im TD-Knoten wird dann der erste Textknoten gesucht. Schließlich werden der TR-Knoten und der (gemäß Spaltentyp passend formatierte) Inhalt des Textknotens als Komponentenpaar ins Hilfs-Array aufgenommen.
- Nun wird das Hilfs-Array sortiert, wobei man der sort-Methode eine eigene Callback-Funktion übergibt. Letztere verwendet abhängig von Spaltentyp und Richtung die passende Vergleichsmethode (alphabetisch/numerisch, aufsteigend/absteigend) für die Zellendaten-Komponente, wodurch die TR-Knoten-Komponente automatisch mitsortiert wird.
- In einer zweiten Schleife werden dann die Elemente des Hilfs-Arrays durchlaufen und die TR-Knoten-Komponenten mittels appendChild() jeweils ans Ende der TBODY-Kinderliste gesetzt. Dies bewirkt ein sukzessives Verschieben von Tabellenzeilen - mit dem Effekt, dass die gewünschte Reihenfolge aufgebaut wird.
Bei manchen Browsern (z.B. beim
Konqueror) wird durch appendChild() das betreffende Element kopiert, und die Kopie wird dann ans Ende der Kinderliste gesetzt; d.h. der Originalknoten bleibt erhalten. Da wir diesen Verdoppelungseffekt hier nicht gebrauchen können, müssen wir das Element vor dem Anhängen erst einmal herauslösen. Dazu benutzen wir die removeChild()-Methode, die uns den entfernten Knoten zurückliefert.
Schon bei der Erzeugung einer TableSort-Instanz wird die betreffende Tabelle übergeben:
var tsort = new TableSort(tabelle);
Falls die Tabelle feste Kopfzeilen besitzt, kann deren Anzahl in einem optionalen zweiten Parameter angegeben werden:
var tsort = new TableSort(tabelle, 1); // mit einer Kopfzeile
Per Default werden alle Zellinhalte als Text interpretiert, d.h. die Sortierung benutzt eine alphabetische Vergleichsmethode. Es ist jedoch möglich, für die einzelnen Spalten auch andere Vergleichsmethoden zu erzwingen. Hierzu übergibt man in einem optionalen dritten Parameter ein Array mit Spaltentypen-IDs:
var tsort = new TableSort(tabelle, 0, [0,1,1,1]); // Spalten: 1 x Text, 3 x Zahl
ID | Zelleninhalt
(Tags/Whitespaces werden ignoriert) | Beispiel |
0 | Text |
Server |
<b>Server</b> |
1 | Dezimalzahl (evtl. mit Nachkomma) |
95 % |
-12.34 |
2 | Ganze Dezimalzahl mit Tausenderpunkten |
1.234.567 |
3 | Datum [Uhrzeit]
Format: tt.mm.jjjj hh:mm:ss |
09.11.2004 |
09-11-2004 23:59 |
Eine Sortierung erfolgt mithilfe der Execute-Methode:
var tsort = new TableSort(tabelle);
tsort.Execute(1); // nach Spalte 1 sortieren
...
tsort.Execute(2, false); // nach Spalte 2 absteigend sortieren
...
tsort.Execute(3); // nach Spalte 3 (immer noch absteigend) sortieren
...
tsort.Execute(1, true); // wieder nach Spalte 1 aufsteigend sortieren
Damit eine Tabelle auch für den Benutzer sortierbar ist, werden i.d.R. ihre Spaltenköpfe interaktiv gemacht: sie reagieren auf Mausklicks und zeigen mit einem kleinen Pfeilsymbol an, nach welcher Spalte und in welche Richtung zuletzt sortiert wurde.
img/i4Dn.gif |
|
Symbol für aufsteigend |
img/i4Up.gif |
|
Symbol für absteigend |
In den Zellen der Kopfzeile muss also das onClick-Event abgefangen werden (hier für eine eigene Funktion HeadClick(), der man die Spaltenposition übergibt).
Ausserdem benötigt man jeweils hinter dem Spaltentitel ein IMG (zunächst mit einer transparenten Platzhalter-Grafik img/i1Spc.gif) für das Pfeilsymbol.
Javascript im Header
function HeadClick(pCol) {
if (!tsort) return; var c=tsort.ColPos();
if (c) document.getElementById('imH'+c).src='img/i1Spc.gif'; //aktuelles Symbol ausblenden
tsort.Execute(pCol,(pCol==c)^tsort.Ascend()); //Sortieren (Richtung umkehren, falls selbe Spalte)
document.getElementById('imH'+pCol).src='img/i4'+(tsort.Ascend()?'Dn':'Up')+'.gif'; //neues Symbol einblenden
}
Weiter unten, im BODY, die Tabelle:
<table id='tsort'>
<tr>
<td onClick='HeadClick(1)'>Vorname <img id='imH1' src='img/i1Spc.gif'></td>
<td onClick='HeadClick(2)'>Nachname <img id='imH2' src='img/i1Spc.gif'></td>
<td onClick='HeadClick(3)'>Abteilung <img id='imH3' src='img/i1Spc.gif'></td>
</tr>
Hier folgen die Datenzeilen, z.B.
<tr>
<td>Dagobert</td>
<td>Duck</td>
<td>Geschäftsleitung</td>
</tr>
... weitere Zeilen ...
</table>
Danach: Javascript zur Initialisierung
var tsort=new TableSort(document.getElementById('tsort'), 1);
Es ist denkbar, dass ein Benutzer auf die Kopfzeile klickt bevor die Tabelle komplett aufgebaut und initialisiert ist - z.B. bei sehr langen Listen oder bei einer langsamen Internetverbindung. In diesem Fall ist die Sortierung natürlich noch nicht aktiviert, und der Benutzer wundert sich.
Um dies zu vermeiden, könnte die Kopfzeile zunächst (im Tag mittels style='DISPLAY:none') unsichtbar sein und erst bei der Initialisierung (mittels Kopfzeile.style.display='';) sichtbar gemacht werden.
Eigenschaften | Beschreibung |
ColPos() |
Liefert die Position [1...N] der zuletzt verwendeten Sortierspalte, bzw. 0 falls noch nicht sortiert wurde. |
Ascend() |
Liefert true, wenn die aktuelle Sortierrichtung aufsteigend ist, sonst false für absteigend. |
Methoden |
Execute() |
Sortierung durchführen.
Parameter:
- col (Integer) Position [1...N] der Sortierspalte.
- [up] (Boolean) optionales Flag: true für aufsteigend, false für absteigend.
- [force] (Boolean) optionales Flag: Sortierung auf jeden Fall durchführen (auch wenn die Tabelle bereits nach Spalte col und Richtung up geordnet wurde).
Standardmäßig wird die Execute()-Anweisung nämlich ignoriert, falls die geforderte Sortierung schon vorliegt. Allerdings kann es vorkommen, dass sich Zelleninhalt ändert - z.B. durch automatisches Aktualisieren oder einen Editiervorgang. Dann muss eine Sortierung evtl. mit den selben Parametern wiederholt werden können.
|
TableSort = function(oTable,nHead,aTyp) {
oTable=oTable.firstChild; nHead=nHead||0; aTyp=aTyp||[];
var myCol=0,myUp=true,myArr;
function txnode(o) { var tn,i=0;
while (oc=o.childNodes[i++]) {
if (oc.nodeType==3) { if (oc.data.replace(/\s+| /g,'').length) return oc; }
else if (tn=txnode(oc)) return tn; }
return null; }
function cellcontent(oTr) { var s='',i,td,o,oc;
for (i=td=0;o=oTr.childNodes[i];i++)
if (o.nodeName=='TD'&&++td==myCol) {
if (oc=txnode(o)) s=oc.data.replace(/\s+| /g,''); break; }
switch (aTyp[myCol-1]) {
case 1: return isNaN(s=parseFloat(s))?0:s;
case 2: return isNaN(s=parseFloat(s.replace(/\./g,'')))?0:s;
case 3: return s.substr(6,4)+s.substr(3,2)+s.substr(0,2)+s.slice(10);
default: return s.toLowerCase().replace(/ä/g,'a').replace(/ö/g,'o').replace(/ü/g,'u').replace(/ß/g,'s'); }}
function compare(a,b) {
switch (aTyp[myCol-1]) { case 1: case 2: return myUp?a[1]-b[1]:b[1]-a[1];
default: return myUp?a[1]<b[1]?-1:a[1]>b[1]?1:0:b[1]<a[1]?-1:b[1]>a[1]?1:0; }}
function initarr(bPur) { var i,tr,o; myArr=[];
for (i=tr=0;o=oTable.childNodes[i];i++) if (o.nodeName=='TR') {
if (tr++<nHead) continue; myArr[myArr.length]=bPur?o:[o,cellcontent(o)]; }}
function invert() { initarr(1); var i=myArr.length-1;
while (i--) oTable.appendChild(myArr[i]); myArr=[]; }
this.ColPos = function() { return myCol; }
this.Ascend = function() { return myUp; }
this.Execute = function(col,up,force) { if (col<1) return; if (up==undefined) up=myUp;
if (col==myCol&&!force) { if (myUp==!!up) return; myUp=!myUp; invert(); return; }
myCol=col; myUp=!!up; initarr(); myArr.sort(compare);
for (var n=myArr.length,i=0;i<n;i++) oTable.appendChild(oTable.removeChild(myArr[i][0])); myArr=[]; }
}
template |
 |