JavaScript History Push State, SPA und die Wiederherstellung

Wie in einer Single Page Application die Navigation vor und zurück über die JavaScript History ermöglicht wird

HTTP-Parameter als Abbild eines Zustandes

Zum Verständnis der Funktionsweise der JavaScript-Funktion history.pushState() ein paar Worte vorab zum Zustandsmodell einer interaktiven Webanwendung. Interaktiv heißt unter Anderem, dass der Benutzer selbst aktiv wird, beispielsweise dadurch dass er bestimmte Eingaben in einem Formular vornimmt. Nach jeder Eingabe gibt es einen Übergang der Anwendung in einen neuen Zustand, wobei dieser Zustandsübergang (Submit) durch bestimmte Parameter charakterisiert ist.

Nun wird der Entwickler der Anwendung die den Zustandsübergang auslösenden Parameter selbst festlegen als sogenannte Schlüsselparameter. In einem Forum wären beispielsweise show und send solche Schlüsselparameter, show=mesgnr zeigt eine bestimmte Nachricht während send eine neue Nachricht erzeugt. Zusätzlich zu diesen Schlüsselparametern kann es weitere, die Zustandsübergänge begleitende Parameter geben.

Diese Ausführungen machen deutlich, dass Parameter nicht nur der Datenübertragung dienen sondern gleichermaßen auch einen bestimmten Zustand bzw. Zustandsübergang einer Webanwendung beschreiben. Und das heißt wiederum, dass anhand bestimmter Parameter auch ein bestimmter Zustand einer Webanwendung wiederhergestellt werden kann. Der Fall, dass alle Parameter an den URL angehängt übertragen bzw. aus diesem gelesen werden beschreibt also nichts weiter als die allbekannte und nüchterne Tatsache, dass jedem URI eine bestimmte Seite zugeordnet und diese Zuordnung eindeutig ist.

Finden nun die Aufrufe verschiedener URI's in einer Browsersitzung statt, wird der Browser diese Folge von Seitenanforderungen intern speichern, was dem Benutzer letztendlich die Navigation vor und zurück ermöglicht. Die nun folgenden Ausführungen werden zeigen, dass es mit JavaScript zusätzlich zum URI und auch unabhängig von diesem eine weitere Möglichkeit gibt zum Anlegen/Wiederherstellen einer Browser-History.

Ein JavaScript-Objekt als Abbild eines Zustandes

Betrachte untenstehenden Code zur Anwendung. Das an history.pushState() übergebene Objekt beeinhaltet sämtliche in das Formular mit dem Index[0] getätigten Eingaben.

// History wird geschrieben
var param_object = input4form(0, 'send', '1');
history.pushState(param_object, 'Titel der Seite', '/jshistory.html?'+ query4request(param_object));

Neben den Titel im zweiten Argument ist der im dritten Argument mitgegebene QUERY_STRING eine String-Präsentation der Formulareingaben. Wie obenstehender Code zeigt, wird Letzterer ja aus dem Parameter-Objekt erzeugt. Somit kann eine bestimmter Zustand der Anwendung anhand der eingegebenen Daten sowohl über das param_object als auch über die in QUERY_STRING serialisierten Parameter wiederhergestellt werden. Eine Wiederherstellung der History mit JavaScript ist nur über das im ersten Argument übergebene Objekt möglich!

Vor- und Zurück Navigation über JavaScript ermöglichen

Gewöhnlich ist JavaScript über Ereignisse gesteuert. Betätigt der Benutzer für die Navigation vor/zurück entsprechende Tasten oder Schaltflächen wird für das window-Objekt das Ereignis popstate gefeuert. Jetzt müssen wir nur noch dafür sorgen, dass in dem Ereignis entsprechenden Handler der in der History gespeicherte Zustand wiederhergestellt wird, etwa so:

// Ereignis zur Benutzeraktion Vor/Zurück
$(window).on('popstate', function() {
    if(this.history.state == null) return;
    else calc(this.history.state);
});

Erläuterung: Jedesmal wenn das Event feuert, beeinhaltet die Eigenschaft history.state das Objekt, was beim Anlegen der History an history.pushState() übergeben wurde (soweit vorhanden, ansonsten null). Genau dieses Objekt wird einer Funktion übergeben, die imstande ist, anhand der im Objekt gespeicherten Daten den Zustand der Anwendung wiederherzustellen. Ein konkretes Beispiel zeigt hier die Funktionsweise. Das Besondere an dieser DEMO ist, dass die Daten im JS-Betrieb sowohl mit Request-Methode POST gesendet als auch aus der History heraus für diese Request-Methode wiederhergestellt werden. Sofern JavaScript für diese Anwendung nicht verfügbar ist, erfolgt ein normales Submit per GET und die Seite wird samt Formular komplett neu geladen.

Navigiert also ein Besucher vor/zurück, wird das Objekt window.history.state zu einem QUERY_STRING serialisiert und per AJAX gesendet. Aus der Response, die sowohl die gesendeten Daten als auch das Ergebnis der Berechnung beinhaltet, wird wieder ein Objekt erstellt, damit das Formular-Template gerendert und ins DOM eingebaut:

// Das Parameter-Objekt wird übergeben und gesendet
function calc(param){
    var xhr = new XMLHttpRequest();
    xhr.open('POST','/jshistory.html');
    xhr.setRequestHeader('x-ajax','1');
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    xhr.onload = function(){
        if( this.status != 200 ) return pretext(this.response);
        var param = qparse(this.response);
        $('#out').html(xr($('#tt').html(),{
            christ:      param.christ[0],
            maya:        param.maya[0],
            gregchecked: param.gregchecked != null ? param.gregchecked[0] : '',
            julichecked: param.julichecked != null ? param.julichecked[0] : ''
        }));
    };
    xhr.send(query4request(param));
}

Unabhängig davon wird bei diesem Event der letzte, in window.history verfügbare URI in die Adresszeile des Browsers geschrieben -- vorausgesetzt, dieser URI wurde an history.pushState() als drittes Argument übergeben.


Anbieter: nmq​rstx-18­@yahoo.de, die Seite verwendet funktionsbedingt einen Session-Cookie und ist Bestandteil meines nach modernen Aspekten in Perl entwickelten Frameworks.