jQuery mobile an sich ist super chic, einfach einzubinden und hat definitiv noch viel vor. Angetreten mit der Mission, dem Coder viele gängige Alltagsprobleme (nicht nur, aber hauptsächlich) auf mobile devices abzunehmen, schafft es (noch) ungewollt Probleme, deren Ursprung für den Programmierer nicht gleich ersichtlich sind. Das Framework “verlangt” uns ab, sich an eine bestimmte HTML-Grundstruktur zu halten, die Anatomie einer Seite wird vorgegeben.

An sich ist das für uns Programmierer kein großes Hindernis. Schnell ins Layout ein paar data-Attribute hinzufügen und los gehts. In der Default-Einstellung des Frameworks ist bereits ajaxLoading aktiviert. Die Seiten werden sehr hübsch geladen, durch Hashtags bleibt der Inhalt bookmarkbar und die Vor-/Zurück-Buttons des Browsers funktionieren wie gewünscht. Benutzt man dabei einen auf der webkit-Engine basierenden Browser, kommt man sogleich in den Genuß von Transitions beim Seitenwechsel. Sofern man ausschließlich jQuery mobile verwendet, wird es dabei auch nicht zu Problemen kommen. Aber wehe dem, der mehr will..

Clientseitiges Caching im DOM

Der in jQuery mobile integrierte Ajax Loader (basierend auf dem (BBQ-Plugin)[http://benalman.com/projects/jquery-bbq-plugin/]) erledigt meiner Meinung nach wesentlich mehr als es soll. Um mit möglichst wenig Datentransfer-Volumen auf dem mobilen Client auszukommen, wird bei einem Request der komplette Seiteninhalt (“.ui-page”) ins DOM hinzugefügt, die vorherige Seite versteckt (nicht entfernt) und per Transitionhandler auf den neuen, aktiven Content umgeschalten. Arbeitet man an irgendeiner Stelle im Page-Content mit IDs, bekommt man jetzt sehr schnell sehr ekelhafte und erstmal nicht nachvollziehbare Probleme oder die Plugins funktionieren einfach nicht mehr.

Was mich hier besonders stört, ist, dass man auf diese Problematik nicht hingewiesen wird (oder ich habe den Hinweis in der Doku übersehen) und man meiner Meinung nach unnötig den Arbeitsspeicher-Verbrauch eines Mobile-Devices aufbläht.

Das Caching selbst ist meiner Meinung nach nur dann unproblematisch, wenn man komplett OHNE ID-Attribut arbeitet sowie die Events nur in der aktiven ui-Page bindet.

Um die Events sauber zu binden, sollte man hier mMn. ausschließlich per traversing arbeiten, dabei ist zu beachten, dass beim allerersten Seitenaufruf keine aktive Seite vorhanden ist. Hier ein möglicher Ansatz:

$body.live('pagecreate', function() {
    (function() {
        return ($.mobile.activepage)
            ? $.mobile.activepage.find('.selector1')
            : $('form');
    })()
    .find('.selector2')
    .callPlugin()
});

Sobald man mit ID-Attributen arbeitet oder man (wie ich) der Meinung ist, dass Browser-Caching nur über HTTP-Header gesteuert werden sollte, bleibt einem nur der komplette Reset des mobile-Caches im pagebeforecreate-Event. Leider muss man hier direkt in die mobile-Internas eingreifen, hoffentlich kann man das mit dem Release einer stabilen Version per Konfiguration abstellen. Aktuell funktioniert der Reset a la:

$body.live('pagebeforecreate', function() {
    $.mobile.urlHistory.stack = [];
    $.mobile.urlstack = [];
    $('.ui-page').not('.ui-page-active').remove();
});

Saubere Server-Response

Obwohl einem das mobile-Framework eigentlich schon alles abnimmt, bin ich der Meinung, dass man auf Server-Seite für den Ajax-Seitenaufruf nicht die komplette Seite ausliefert. Auch wenn das “Rausballern” der kompletten Seite funktionieren mag, sollte man hier die Response möglichst klein halten. Daraus folgt: Doctype, meta - sowie script tags und dergleichen komplett rausschmeißen! Einzig und allein das <title />-Tag sowie der Page-Content reichen vollkommen aus.

Aufräumen nicht vergessen

Unabhängig davon, ob man nun mit oder ohne DOM-Caching arbeitet, wenn man den Ajax-Loader verwendet, sollte man unbedingt “sauber machen” ( bevor man den Cache wegwirft ). Man(n) geht auch nicht bei der Freundin auf die Toilette und spült dann nicht runter! 

Sofern man jQueryUI-Plugins verwendet, ist das per pseudo-Selector schnell erledigt.

  • $( “:namespace-widgetname” ) findet alle aktiven Instanzen, z.B. beim Verwenden von jqueryUI-slider im pagebeforecreate-Event

  • $( ‘:ui-slider’ ).slider( ‘destroy’ ); aufrufen.