JavaScript/Anwendung und Praxis/Tabellen dynamisch sortieren

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Bei tabellarischen Daten muss schon beim Erstellen der Tabelle festgelegt werden, in welcher Sortierung die Daten dargestellt werden sollen. Soll durch eine Benutzeraktion die Reihenfolge geändert werden, benötigen Sie bei statischem HTML für jede Sortierung ein eigenes HTML-Dokument, bei dynamisch aus z.B. einer Datenbank erstellten Tabellen können die Daten serverseitig umsortiert werden.

Diese Sortierung kann aber auch mittels Javascript ohne Serverunterstützung im Browser durchgeführt werden. Dieser Artikel beschreibt, wie dabei vorzugehen ist.

Inhaltsverzeichnis

[Bearbeiten] Die Tabelle im HTML

Der folgende HTML-Code zeigt eine Tabelle mit fünf Spalten. Die Tabelle soll nach jeder dieser Spalten durch einen Klick auf die entsprechende Spaltenüberschrift sortiert werden. Über das class-Attribut wird dem Script mitgeteilt, dass diese Tabelle sortierbar sein soll. Beachten Sie, dass an dieser Stelle ausschließlich das HTML vorgestellt wird, die Tabelle also noch nicht sortierbar ist. Wie dies umzusetzen ist, lernen Sie im weiteren Verlauf dieses Artikels.

Beispiel ansehen …
<table class="sortierbar">
	<thead>
		<tr>
			<th>Lfd. Nr.</th>
			<th>Name</th>
			<th>Punkte</th>
			<th>Note</th>
			<th>Bemerkung</th>
		</tr>
	</thead>
	<tfoot>
		<tr>
			<td colspan=5>Stand: 1. 11. 15</td>
		</tr>
	</tfoot>
	<tbody>
		<tr>
			<td>1</td>
			<td>Matthias</td>
			<td>9</td>
			<td>2.5</td>
			<td>Normal</td>
		</tr>
		<tr>
			<td>2</td>
			<td>Gunnar</td>
			<td>12</td>
			<td>1.2</td>
			<td><b>Fett</b></td>
		</tr>
		<tr>
			<td>3</td>
			<td>Christian</td>
			<td>8</td>
			<td>3.5</td>
			<td><i>Kursiv</i></td>
		</tr>
		<tr>
			<td>4</td>
			<td>Janosch</td>
			<td>11</td>
			<td>1,4</td>
			<td><a href="#">Link</a></td>
		</tr>
		<tr>
			<td>5</td>
			<td>Raoul</td>
			<td>10</td>
			<td>2.1</td>
			<td><b><a href="#">Fetter Link</a></b></td>
		</tr>
		<tr>
			<td>6</td>
			<td>Jürgen</td>
			<td>7</td>
			<td>3.9</td>
			<td><h2>Überschrift</h2></td>
		</tr>
	</tbody>
</table>

[Bearbeiten] Das Javascript

Zuerst muss das Script die zu sortierenden Tabellen finden. Dieses erfolgt mit der Methode querySelectorAll. Danach wird für jede Tabelle das Objekt tableSort angelegt. Damit sichergestellt ist, dass die Tabelle auch schon angelegt ist, wird die Suche in die Funktion initTableSort gelegt und mit addEventListener als Eventlistener zum Event DOMContentLoaded hinzugefügt.

Beispiel
var tableSort = function (tab) {
 
 
} // tableSort
 
 
var initTableSort = function () {
  var sort_Table = document.querySelectorAll("table.sortierbar");
  for (var i = 0; i < sort_Table.length; i++) new tableSort(sort_Table[i]);
} // initTableSort
 
window.addEventListener("DOMContentLoaded",initTableSort,false);

Die Sortierung soll durch einen Klick auf die Spaltenüberschrift ausgelöst werden. Dazu könnte man den th-Felder im thead-Bereich der Tabelle einen Eventhandler für das click-Event geben. Dann wäre aber der Sortierer mit der Tastatur nicht zu bedienen. Daher setzen wir in die th-Felder einen Button mit deren Inhalt und deren Aussehen. Diesem Button geben wir dann den Eventhandler für das click-Event:

Beispiel
var titel = tab.getElementsByTagName("thead")[0].getElementsByTagName("tr")[0].getElementsByTagName("th");
 
var sortbuttonStyle = document.createElement("style"); // Stylesheet für Button im TH
sortbuttonStyle.innerText = ".sortbutton { width:100%; height:100%; border: none; background-color: transparent; font: inherit; color: inherit; text-align: inherit; padding: 0; cursor: pointer; } .sortbutton::-moz-focus-inner { margin: -1px; border-width: 1px; padding: 0; }";
document.head.appendChild(sortbuttonStyle);
 
var initTableHead = function (sp) { // Kopfzeile vorbereiten
  var b = document.createElement("button");
  b.type = "button";
  b.className = "sortbutton";
  b.innerHTML = titel[sp].innerHTML;
  b.addEventListener("click",function () { tsort(sp); }, false);
  titel[sp].innerHTML = "";
  titel[sp].appendChild(b);
}
 
for (var i = 0; i < titel.length; i++) initTableHead(i) ;

Zum Sortieren wird die Arraymethode sort verwendet. Dazu muss der Inhalt der HTML-Tabelle in ein 2D-Array kopiert werden. Zusätzlich wird auch noch die Referenz auf die Tabellenzeile in das Array kopiert. Ein 2D-Array kann man als 1D-Array ansehen, dessen Elemente wieder 1D-Arrays sind:

Beispiel
var arr = new Array(nzeilen);
for (var z = 0; z < nzeilen; z++) arr[z] = new Array(nspalten);

Der Zugriff auf die Tabellenzeilen erfolgt über die Methode rowsdes TableObjects:

Beispiel
var tbdy = tab.getElementsByTagName("tbody")[0];
var tz = tbdy.rows;
var nzeilen = tz.length;
if (nzeilen == 0) return;
var nspalten = tz[0].cells.length;
var arr = new Array(nzeilen);
 
for (var z = 0; z < nzeilen; z++) {
  var zelle = tz[z].getElementsByTagName("td"); // cells;
  arr[z] = new Array(nspalten+1);
  arr[z][nspalten] = tz[z];
  for (var s = 0; s < nspalten; s++) {
    var zi = getData(zelle[s]);
    arr[z][s] = zi ;
  }
}

Das Auslesen der Tabellenfelder erfolgt in der Funktion:

Beispiel
var getData = function (ele) {
  return ele.innerHTML;
}

Zum Sortieren wird die Array-Methode sort verwendet. Da ein 2D-Array sortiert werden soll, müssen in der Vergleichsfunktion der Methode sort die Werte aus den Zeilen-Arrays miteinander verglichen werden, die zur Spalte gehören, nach der sortiert werden soll:

Beispiel
var vglFkt = function (a, b) {
  var as = a[sortiert], bs = b[sortiert];
  if (as > bs) return 1;
  else return -1;
}

In der Sortierfunktion tsort wird dann nur noch überprüft, ob die Tabelle schon nach der gewünschten Spalte sortiert wurde, dann wird mit der Array-Methode reverse nur die Reihenfolge umgedreht, sonst wird sortiert und gespeichert, nach welcher Spalte sortiert wurde:

Beispiel
var tsort = function (sp) {
  if (sp == sortiert) arr.reverse(); // Tabelle ist schon nach dieser Spalte sortiert, also nur Reihenfolge umdrehen
  else { // Sortieren
    sortiert = sp;
    arr.sort(vglFkt);
  }      
  for (var z = 0; z < nzeilen; z++) tbdy.appendChild(arr[z][nspalten]); // Sortierte Daten zurückschreiben
}

Das vollständige Script sieht jetzt so aus:

Beispiel ansehen …
( function () {
 
  "use strict";
 
  var tableSort = function (tab) {
    var titel = tab.getElementsByTagName("thead")[0].getElementsByTagName("tr")[0].getElementsByTagName("th");
    var tbdy = tab.getElementsByTagName("tbody")[0];
    var tz = tbdy.rows;
    var nzeilen = tz.length;
    if (nzeilen == 0) return;
    var nspalten = tz[0].cells.length;
    var arr = new Array(nzeilen);
    var sortiert = -1;
 
    var sortbuttonStyle = document.createElement("style"); // Stylesheet für Button im TH
    sortbuttonStyle.innerText = ".sortbutton { width:100%; height:100%; border: none; background-color: transparent; font: inherit; color: inherit; text-align: inherit; padding: 0; cursor: pointer; } .sortbutton::-moz-focus-inner { margin: -1px; border-width: 1px; padding: 0; }";
    document.head.appendChild(sortbuttonStyle);
 
    var initTableHead = function (sp) { // Kopfzeile vorbereiten
      var b = document.createElement("button");
      b.type = "button";
      b.className = "sortbutton";
      b.innerHTML = titel[sp].innerHTML;
      b.addEventListener("click", function () { tsort(sp); },false);
      titel[sp].innerHTML = "";
      titel[sp].appendChild(b);
    }
 
    var getData = function (ele) {
      return ele.innerHTML;
    }
 
    var vglFkt_s = function (a, b) {
      var as = a[sortiert], bs = b[sortiert];
      if (as > bs) return 1;
      else return -1;
    } // vglFkt_s
 
    var tsort = function (sp) {
      if (sp == sortiert) arr.reverse(); // Tabelle ist schon nach dieser Spalte sortiert, also nur Reihenfolge umdrehen
      else { // Sortieren
        sortiert = sp;
        arr.sort(vglFkt_s);
      }      
      for (var z = 0; z < nzeilen; z++) tbdy.appendChild(arr[z][nspalten]); // Sortierte Daten zurückschreiben
    } // tsort
 
    // Kopfzeile vorbereiten
    for (var i = 0; i < titel.length; i++) initTableHead(i) ;
 
    // Tabelleninhalt in ein Array kopieren
    for (var z = 0; z < nzeilen; z++) {
      var zelle = tz[z].getElementsByTagName("td"); // cells;
      arr[z] = new Array(nspalten +1);
      arr[z][nspalten] = tz[z];
      for (var s = 0; s < nspalten; s++) {
        var zi = getData(zelle[s]);
        arr[z][s] = zi ;
        // zelle[s].innerHTML += "<br>" +zi; // zum Debuggen
      }
    }
 
  } // tableSort
 
 
  var initTableSort = function () { 
    var sort_Table = document.querySelectorAll("table.sortierbar");
    for (var i = 0; i < sort_Table.length;i++) new tableSort(sort_Table[i]);
  } // initTable
 
  if (window.addEventListener) window.addEventListener("DOMContentLoaded", initTableSort, false); // nicht im IE8
 
})();

Dabei werden beim Zurückschreiben nicht die Tabellenfelder einzeln zurückkopiert. Statt dessen werden die Tabellenzeilen, deren Referenzen ja auch in das 2D-Array geschrieben wurden, einfach in der neuen Reihenfolge mit appendChild in den tbody eingehängt.

Das Sortieren nach den ersten beiden Spalten funktioniert, aber bei der dritten und vierten Spalte versagt der Algorithmus. Das liegt daran, dass die Methode innerHTML den Inhalt der Tabellenzellen als String liest. Daher werden die Zahlen als Buchstabenfolge sortiert und 10 ist so kleiner als 9. Es muss also beim Lesen der Tabellenfelder geprüft werden, ob eine Zahl oder ein String vorliegt. Die modifizierte Funktion getData sieht jetzt so aus:

Beispiel ansehen …
var getData = function (ele, s) {
var val = ele.innerHTML;
if (!isNaN(val) && val.search(/[0-9]/) != -1) return val;
var n = val.replace(",", ".");
if (!isNaN(n) && n.search(/[0-9]/) != -1) return n;
  sorttype[s] = "s"; // String
  return val;
} // getData

Mit dieser Erweiterung können jetzt Zahlen oder Texte sortiert werden, sobald sich in den Tabellenzellen aber HTML-Elemente befinden, wie im Beispiel in der rechten Spalte, funktioniert das Sortieren noch nicht richtig. Dieses liegt daran, das wir zum Auslesen die Methode innerHTML verwenden. Hier werden die Tags mit in die Sortierung einbezogen. Wenn wir innerHTML durch textContent ersetzen, wird nur noch der Inhalt der Elemente ohne die Tags ausgelesen:

Beispiel ansehen …
var getData = function (ele, s) {
  var val = ele.textContent;
  if (!isNaN(val) && val.search(/[0-9]/) != -1) return val;
  var n = val.replace(",", ".");
  if (!isNaN(n) && n.search(/[0-9]/) != -1) return n;
  sorttype[s] = "s"; // String
  return val;
} // getData

[Bearbeiten] Fazit

Dieser Tabellensortierer liefert in vielen Fällen schon zufriedenstellende Ergebnisse. Allerdings sind z.B. Umlaute oder Datums- und Zeitangaben noch nicht berücksichtigt. Auch können Sie noch nicht wählen, nach welchen Spalten sortiert werden soll. Einen deutlich umfangreicheren Tabellensortierer finden Sie unter http://www.j-berkemeier.de/TableSort.html.

[Bearbeiten] Weblinks

Meine Werkzeuge
Namensräume

Varianten
Aktionen
Übersicht
Index
Mitmachen
Werkzeuge
Spenden
SELFHTML