HTML 5 und Javascript 5: Clientseitige Datenbanken

Nachdem sich die Web Hypertext Application Technology Working Group (WHATWG) 2004 gegründet hat, die aus einigen bedeutenden Internet-Riesen besteht (u.a. Apple, Mozilla, Opera), kommt wieder Fahrt in neue HTML- und Javascript-Standards. Die Gruppe hat viele Proposals erstellt, von denen jetzt einige vom W3C übernommen werden. Stichworte sind da HTML 5, ECMAScript 5, Web Workers, Web Storage, Web Sockets und einiges mehr.

Web Sockets wird die Pushing-Technik werden, mit der Server bei Änderungen Nachrichten an den Browser schicken können, dann erübrigt sich das derzeitige Pullen und Nachfragen via AJAX, ob es Neuigkeiten gibt. Das entlastet die Server und spart Traffic, und Nachrichten kommen genau dann im Browser an wenn sie auf dem Server passieren, nicht erst beim nächsten Pull.

Heute soll es um den Web Storage gehen, die clientseitige Datenbank im Browser. In der Vergangenheit konnte man Daten nur auf der aktuellen Seite in Javascript-Variablen vorhalten, sobald aber der nächste Full-Page-Reload kommt sind die Daten futsch. Die zweite Möglichkeit waren Cookies, in denen man Daten speichern kann. Da aber Cookies bei jedem Request mitgesendet werden, ist das kein guter Speicherort für größere Datenmengen, Cookies sollte man höchstens für eine SessionID nutzen.

HTML 5 bietet uns die Möglichkeit, mittels des localStorage einfache Daten im Client zu speichern. Hier ein kleines Beispiel:

<!DOCTYPE html>


    <script type="text/javascript">
    function store() {
        var key = document.getElementById('key').value;
        var data = document.getElementById('data').value;
        localStorage.setItem(key, data);
      }
     function read() {
       var key = document.getElementById('key').value;
       document.getElementById('data').value = localStorage.getItem(key);
     }
    </script>


    
    

Dieser Code funktioniert bereits im IE 8, Firefox 3.5, Safari 4. Auch wenn man zwischendurch wegsurft und dann wiederkommt sind die Daten noch auslesbar. Wenn man allerdings 2 Fenster öffnet und in beiden Fenstern den selben Key speichert, überschreibt man den jeweils anderen Wert. Um das zu vermeiden (also beide Fenster zu trennen) gibt es den sessionStorage. Wenn man im obigen Code den localStorage durch den sessionStorage austauscht, kommen sich die beiden Fenster nicht mehr in die Quere, allerdings sind die Daten futsch wenn man den Tab schliesst. Für eine permanente Speicherung, auf die alle Tabs zugreifen können, nutzt man also den localStorage.

Bei diesem Feature handelt es sich allerdings „nur“ um einen einfachen Key-Value-Store, wir können keine komplexen Abfragen oder ähnliches erstellen.

In Google Gears wurde es bereits vorgemacht: Idealerweise brauchen wir eine clientseitige Datenbank, die wir via SQL füllen und abfragen können, um Daten auf den Client auszulagern und so den Server zu entlasten und Daten nicht bei jedem neuen Besuch neu laden zu müssen. Stattdessen werden Daten, die einmal den Client erreicht haben, in die Datenbank gelegt, die dann auch sehr schnell ausgelesen, sortiert und gruppiert werden können, also eine vollwertige Datenbank im Browser.

Mit ECMA-Script 5 (im Volksmund Javascript 5) wird das möglich sein. Der folgende Code funkioniert aktuell nur in WebKit/Safari, für Firefox und Chrome gibt es aber bereits funktionierende Beta-Implementierungen:

<!DOCTYPE html>


    <script type="text/javascript">
    if (!window.openDatabase) {
        alert( "Browser does not support database storage.");
    } else {
        db = openDatabase('testDb', '1.0', 'testDb', 65536);
    }
 
    function create() {
        db.transaction(
            function (tx) {
                tx.executeSql('CREATE TABLE todo (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, description TEXT NOT NULL DEFAULT "", todo_order INTEGER NOT NULL DEFAULT 0, done INTEGER NOT NULL DEFAULT 0 );',
                    [], function (tx, result1) {
                        alert("successfully created database");
                    }, errorHandler
                );
            }
        );
    }
 
    function insert() {
        db.transaction(
            function(tx) {
                tx.executeSql('INSERT into todo (description, todo_order) VALUES ( ? , ? );',
                    [ document.getElementById('firstKey').value , 3 ],
                    function (tx, resultSet) {
                        if (!resultSet.rowsAffected) {
                            // Previous insert failed. Bail.
                            alert('No rows affected!');
                            return false;
                        }
                        alert('insert ID was '+resultSet.insertId);
                    }, errorHandler
                );
            }
        );
    }
 
    function selectnow() {
        db.transaction(
            function(tx) {
                tx.executeSql('SELECT description FROM todo WHERE done=0', [], function(tx, results) {
                    // do some more stuff
                    console.log("Results returned: "+results.rows.length);
                    var resultString = '';
                    for (var i=0; i';
                    }
                    document.getElementById('latestUpdated').innerHTML = resultString;
                    alert("My first database query finished executing!");
                }, errorHandler);
            }
        );
    }
 
    function drop() {
        db.transaction(
            function(tx) {
                tx.executeSql('DROP Table todo', [], function(tx, result1) {
                    alert("successfully deleted database!");
                }, errorHandler);
            }
        );
 
    }
 
    function errorHandler(transaction, error)
    {
        // error.message is a human-readable string.
        // error.code is a numeric error code
        alert('Oops.  Error was '+error.message+' (Code '+error.code+')');
    }
    </script>


    

Create Table

Insert Values

Select Values


Drop Table

Im Safari sieht das dann so aus:

Eine sehr gute Quelle zu diesem Thema ist diese Seite bei Apple über die Javascript Database.

Weitere Interessante Features von HTML 5 und ECMA-Script 5: Strict Mode, getter und setter bei Objekten, Objekte „einfrieren“, neue Html-Tags, neue Drag&Drop Funktionen, postMessage() und vieles mehr.

Einiges davon funktioniert bereits im Firefox 3.5, Chrome, Safari und auch im IE8. Beispielsweise:

<!DOCTYPE html>



    <video src="video/testvideo.mp4" controls autobuffer autoplay>



Deprecated: Directive 'allow_url_include' is deprecated in Unknown on line 0