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.

Interne Funktionsweise

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:

Erzeugung

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
0Text   Server
<b>Server</b>
1Dezimalzahl (evtl. mit Nachkomma) 95 %
-12.34
2Ganze Dezimalzahl mit Tausenderpunkten 1.234.567
3Datum [Uhrzeit]
Format: tt.mm.jjjj hh:mm:ss
09.11.2004
09-11-2004 23:59

Verwendung

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

Interaktive Kopfzeile bereitstellen

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&auml;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 und Methoden

EigenschaftenBeschreibung
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.

Modul-Quellcode

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+|&nbsp;/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+|&nbsp;/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=[]; }
}

Index :: Javascript


template