Tabellen per Javascript filtern
Wenn Google versagt… muss man selber ran! Jedenfalls fand ich zu meinem konkreten Problem keine wirkliche Lösung.
Was ist das Problem? Nun, ich will eine Tabelle filtern, mit Javascript. Soweit kein Problem, dazu findet man unzählige Lösungen, z.B. http://www.vonloesch.de/node/23.
Nur versagen diese Lösungen alle, wenn man in einer Tabelle Zellen über mehrere Zeilen zusammengefasst hat.
Wenn der Suchbegriff in einer der Zeilen ist, die „unter“ einer Zeile mit rowspan liegen, diese Zeile also weniger Zellen hat (technisch, optisch natürlich nicht) und alle anderen
Zeilen ausgeblendet werden, dann hat man plötzlich nicht mehr drei Zellen, sondern z.B. nur noch eine:
Dieser Fall ist ja sogar noch ertragbar, blöd wird es wenn da noch andere Zeilen dazu kommen und die Tabelle auf einmal so ganz anders aussieht…
Was also tun? Prinzipiell sollte es ja so sein, das Zeilen die über rowspan verbunden sind, auch zusammen gehören. Daher wäre es ja vielleicht ganz gut, wenn man diese Zeilen auch alle anzeigen würde auch wenn der Suchbegriff nur in einer der Zeilen auftaucht? Also so:
Einen Teil meiner Lösung habe ich bei Stackoverflow gefunden. Die Grundlage bildete ein Dokuwiki-Plugin, von dessen Javascript ist aber eigentlich nichts mehr übrig geblieben. Da ich das für ein Dokuwiki brauche, gibt es ein paar „Seltsamkeiten“, wie das ich jQuery anstatt $ schreiben muss.
Meine Lösung hat auch nicht alle Fälle berücksichtig, bei komplizierteren Konstrukten dürfte das sicher aussteigen. Da diese aber über die verfügbare Wikisyntax nicht herstellbar sein dürften, ist das hinreichend gut. 😉
searchtable = {
filterall : function(term, _id) {
var searchstr = term.value.toLowerCase();
var table = jQuery(jQuery('#' + _id + ' table')[0]); // just our table
var tds = table.find('td'); // all td
var trs = table.find('tr'); // all tr
var first = jQuery(trs[0]); // first row should be our header
var tdc = first.children().length; // count the number of ths in first row
// remove all old search hits
jQuery('span.search_hit').each(function() {
jQuery(this).replaceWith(jQuery(this).text());
});
// show everything if searchstr is empty
if (searchstr.length == 0) {
trs.show();
return;
}
// mark each searchstr
var re = new RegExp("(" + searchstr + ")", "ig");
tds.filter(function() {
return this.innerHTML.toLowerCase().indexOf(searchstr) >= 0
}).each(function() {
if (jQuery(this).children().length < 1) {
jQuery(this).html(
jQuery(this).text().replace(re,'<span class="search_hit">$1</span>')
)
}
});
// find all tds which contain the searchstr, case insensitive
var toShow = tds.filter(function() {
return this.innerHTML.toLowerCase().indexOf(searchstr) >= 0
});
// hide all rows with no results
trs.not(trs.has(toShow)).hide();
// always show first row!
first.show();
// do some magic for rowspans
// we want to show each rowspan totally, so our table doesnt break anymore
toShow.each(function() {
var father = jQuery(this).parent();
// if this is a "child" of an rowspan-tr, try to find our "parent"
// find first tr above our tr which has an rowspan-td
if (father.find('td').length < tdc) {
father = father.prevAll('tr').find('td[rowspan]').eq(0).parent();
}
// find all td with rowspan
var rspan = father.find('td[rowspan]');
if (rspan.length) {
// ok, just use only the first one... there may be more, but we simply ignore them
rspan = parseInt(jQuery(rspan[0]).attr('rowspan'));
father.nextUntil(':nth-child(' + (father.index() + rspan + 1) + ')').andSelf().show();
}
});
},
};
Der Code ist kommentiert (damit ich den auch noch in 3 Monaten blicke… 😉 ), trotzdem kurz erklärt:
Zeile | Beschreibung |
3-8 | Ein paar Initialisierungen |
10-12 | Entferne alle alten Suchmarkierungen |
15-18 | Wenn es keinen Suchstring gibt mach alles sichtbar und mach Schluss |
21-30 | Markiere alle Vorkommen des Suchstrings |
33-35 | Suche alle td die den Suchstring enthalten |
38 | Verstecken alles andere |
41 | Da die erste Zeile immer der Header ist: zeige sie immer an |
46 | Gehe durch alle gefundenen td |
47 | Hol das Elternelement des aktuellen td, sollte ein tr sein |
51 | Wenn das aktuelle tr weniger td hat als die erste Zeile ist das vermutlich einem rowspan geschuldet |
52 | „Spule“ durch alle tr zurück, bis du eins findest, das ein td mit rowspan enthält. |
55 | Hole alle td mit rowspan aus dem aktuellen tr, das muss nicht mehr das tr-Elternelement des aktuellen td sein |
58 | Hol dir die Anzahl an Zeilen über das sich dieser rowspan spannt |
59 | Ausgehend von der aktuellen Position gehe die gefundene Anzahl an Zeilen runter und lasse all diese Zeilen wieder auftauchen |
Kommentare erwüscht! Da ist sicher noch Optimierungspotential drin. 😉
Filed under: Coding - @ 18.04.2013 15:08
Schlagwörter: HTML, Internet, javascript, Programmieren, webdev