Donnerstag, 9. April 2009

Kleine Javascript/CSS Bastelei: Zusammenhängende Einträge in einer großen Tabelle kennzeichnen

P1020986 Wir haben eine oft recht umfangreiche Tabelle in der eKVV Suche nach freien Räumen. In jeder Zelle werden die freien Räume gezeigt, wobei der gleiche Raum in unterschiedlichen Zellen an unterschiedlicher Position stehen kann. Dies lässt sich nicht verhindern, da die Tabelle sonst noch größer werden würde.

Damit die BenutzerInnen trotzdem einfach sehen können, ob der Raum in anderen Zellen (also zu anderen Zeiten) verfügbar ist wird mit Javascript und CSS folgende Funktionalität implementiert:

  • Sobald der Mauszeiger auf einen bestimmten Raum geht werden alle Vorkommnisse des Raums in der Tabelle anders formatiert.
  • Sobald der Mauszeige den Raum wieder verlässt wird die alte Formatierung wieder hergestellt.

Vorbereitung der gemeinsamen Formatierung: CSS Klassen

Damit die Formatierung aller Vorkommnisse eines Raumes leicht per CSS gemacht werden kann, erhalten die Links der Räume eine weitere CSS Klasse, die sich aus dem Raumnamen ableitet. Beispiel:

<a href=”..” class=”… raum_h1”>…</a>

Da in den Raumnamen prinzipiell alle Zeichen zugelassen sind muss eine Bereinigung durchgeführt werden. In der Bereinigung werden alle Zeichen bis auf a-z und 0-9 entfernt.

Mit dieser Vorbereitung können über eine CSS Anweisung wie der Folgenden alle Stellen mit dem gleichen Raum markiert werden:

a.raum_h1 {
     border: 1px solid black;
}

Hier sind der Phantasie natürlich keine Grenzen gesetzt. Die Manipulation des Stylesheets hat gerade bei großen Tabellen Performancevorteile im Vergleich zur Manipulation jedes einzelnen zu formatierenden Elements, z. B. durch Hinzufügen/Entfernen einer besonderen Klasse zu allen Links des gleichen Raums in den onmouseover/onmouseout Events.

Javascriptfunktionen für die Events onmouseover und onmouseout

Alles was wir jetzt noch brauchen sind zwei Eventhandler auf dem Anchortag:

  1. onmouseover: Beim ‘Betreten’ des Raumbereiches mit dem Mauszeiger muss die Markierung per CSS aktiviert werden
  2. onmouseout: Beim Verlassen muss die Markierung deaktiviert werden, damit nicht nach und nach alle Räume gekennzeichnet bleiben

Da es sehr viele Links gibt lagern wir die beiden Aktionen in eigene Funktionsdefinitionen aus. Das spart viel Platz im generierten HTML Code. Die Anchortags sehen dann so aus:

<a class=".. raum_h11"
          onmouseover="javascript:zr('raum_h11');"
          onmouseout="javascript:vr();" …

Jetzt müssen wir nur noch die Funktionen zr (für zeige Raum) und vr (für verberge Raum) implementieren und im HTML Code der Seite unterbringen. Warum beim Aufruf von vr kein Parameter übergeben wird? Das sehen wir gleich.

Nützlich Hinweise zur Manipulation der Stylesheets per Javascript finden sich z. B. im DADABase Blog und in den javascript.faqts. Mit diesen Hilfen bekommt man schnell die notwendigen Funktionen hin. Das wesentliche DOM Element ist dabei document.styleSheets:

<!-- Dieses leere STYLE Tag wird gebraucht für die folgenden JS Funktionen -->
<style></style>
<script type="text/javascript">
    function zr(cssClass) {
        vr();
        var sSheet = document.styleSheets[document.styleSheets.length-1];
        var selector = "a." + cssClass;
        var rule = "border: 1px solid black;";
        sSheet.insertRule("" + selector + " { " + rule + " }", 0);
    }
    function vr() {
        var sSheet = document.styleSheets[document.styleSheets.length-1];
        while (sSheet.cssRules && sSheet.cssRules.length > 0) {
            sSheet.deleteRule(0);
        }
    }
</script>

Wir machen es uns hier einfach und greifen mit document.styleSheets[document.styleSheets.length-1] direkt auf das extra für diesen Zweck angelegte, zunächst leere STYLE Tag zu. Falls noch andere STYLE Tags folgen sollten funktioniert das natürlich nicht so einfach!

Dieses Konstrukt funktioniert wunderbar in Firefox 3.0.8, in Chrome 2.0.x, in Safari 4 Public Beta und in Opera 9.64. Aber natürlich funktioniert es deshalb noch lange nicht im Internet Explorer. Und das tut es auch in der Tat nicht im MSIE 8.

Der Grund liegt darin, dass der MSIE kein insertRule kennt, sondern eine addRule Funktion. Das gleiche gilt auch für die Funktionen zum Entfernen von Rules. Ein Beispiel, wie man damit umgehen kann, findet sich in CodingsForums.com. Ein noch raffinierterer Trick wird auf TUTORIALHELPDESK.COM beschrieben. Dort wird für den MSIE das Stylesheetobjekt einfach um die noch fehlenden Funktionen ergänzt. Dieser Ansatz der einheitlichen Methoden hat nur beim Hinzufügen der Rules seine Grenze, da insertRule weniger Parameter erwartet als addRule

Mit diesem Wissen ausgestattet kann man nun die Funktionen so erweitern, dass sie auch im MSIE funktionieren. Hier das Ergebnis, welches nun auch im MSIE 8 funktioniert:

   <!-- Dieses leere STYLE Tag wird gebraucht für die folgenden JS Funktionen -->
   <style></style>

    <script type="text/javascript"> 
        function zr(cssClass) {
        vr();
        var sSheet = provideSSheet();
        var selector = "a." + cssClass;
        var rule = "border: 1px solid black;";
        try {           
            sSheet.insertRule("" + selector + " { " + rule + " }", 0);
        } catch (msie) {
            try {
                sSheet.addRule(selector, rule);
            } catch (err) {
            }
        }
    }
    function vr() {
        var sSheet = provideSSheet();
        while (sSheet.cssRules && sSheet.cssRules.length > 0) {
            sSheet.deleteRule(0);
        }
    }
    function provideSSheet() {
        var sSheet = document.styleSheets[document.styleSheets.length-1];
        // Check to see if we are operating in Internet Explorer
        if (sSheet.rules)
        {
            // Map the standard DOM attributes and methods to the internet explorer
            // equivalents
            sSheet.cssRules = sSheet.rules; 
            sSheet.deleteRule = function(ruleIndex)
            {
                 this.removeRule(ruleIndex);
            };
        }
        return sSheet;
    }   
</script>

Damit sind wir fertig! Und so sieht es dann aus:

ScreenShot_eKVV_Raumfreisuche

Erfahrungen

Die Lösung scheint in verschiedenen Browsern unterschiedlich schnell zu reagieren. Hier ein paar erste Erfahrungen:

Firefox: Grundsätzlich am problemlosesten. Alles ist flüssig.

Opera: Ähnlich wie Firefox.

Chrome/Safari: Der allererste Aufruf der JS Funktionen braucht eine gewisse Zeit, danach ist alles flüssig

MSIE 8: Nie wirklich schnell, das rasche Scrolling über die Seiten wird manchmal behindert durch die JS Funktionen

Kommentar veröffentlichen