Bunter Hund

jQuery Mobile – Single-Page-Applications realisieren
Kommentare

Wer von nativer Entwicklung auf HTML5/JavaScript umstellt, steht ohne GUI-Stack da. Es gibt kaum ein natives SDK, das keine Möglichkeit zum Realisieren von Benutzerschnittstellen mitliefert – bei der Arbeit mit dem „offenen Web“ müssen Sie das GUI anhand von CSS realisieren.

Aufgrund der in den letzten Jahren wesentlich gestiegenen Ansprüche der Nutzer ist das für den durchschnittlichen Entwickler nicht oder nur mit sehr großem Aufwand zu bewerkstelligen. Da dieses Problem immer wieder auftritt, entstand im Laufe der Zeit eine Gruppe von als Frameworks bezeichneten Bibliotheken mit „Musterlösungen“. jQuery Mobile stammt aus der Feder des für die weltbekannte jQuery-Bibliothek bekannten Entwicklerteams. Nicht zuletzt aus diesem Grund hat sich das Framework im Laufe der letzten Monate als Quasistandard in Sachen Benutzerschnittstellen etabliert.

Single-Page forever

Traditionelle Webseiten wurden meist aus mehreren HTML-Seiten aufgebaut, die miteinander durch Hyperlinks verbunden waren. Als Stuart Morris im Jahr 2002 seine „Self-Contained Website“ auf die Bevölkerung losließ, dachte er mit Sicherheit nicht daran, dass dieses Konzept einige Jahre später absolute Marktherrschaft erreichen würde. Aus Sicht eines mit jQuery Mobile arbeitenden Entwicklers ist die Erstellung einer Single-Page-Applikation in höchstem Maße wünschenswert. Da die Webseite beim Wechsel des aktiven „Frames“ nicht neu geladen wird, bleiben die diversen globalen Variablen „am Platz“. Zudem lässt sich ein lokaler Wechsel weitaus leichter animieren, da die Bewegung der Steuerelemente durch vom Framework bereitgestellten Code vorgenommen werden kann.

Nach diesen einführenden Überlegungen ist es nun an der Zeit, unsere erste jQuery-Mobile-„Applikation“ zu realisieren. Dazu brauchen wir die aktuellste Version des Frameworks, die sich hier herunterladen lässt. Entpacken Sie das .zip-Archiv danach an eine für Sie bequeme Stelle. Mit der Option „Custom Download“ können Sie den jQuery Mobile Builder aktivieren, der das Erstellen von „benutzerdefinierten“ Distributionen erlaubt. Diese unterscheiden sich von der „Normalvariante“ insofern, als sie nur die von ihnen benötigten Funktionen enthalten. Leider kann das Framework dies nicht automatisch feststellen, weshalb sie die verwendeten Bereiche selbst angeben müssen – das Vergessen eines Moduls führt in diesem Fall zu einer nicht lauffähigen Applikation. jQuery Mobile wird aus Gründen der Einfachheit ohne eine Version der Mutterbibliothek ausgeliefert, und ist im Grunde genommen auch ohne sie lebensfähig. Im Unterordner /demos/js/ findet sich eine passende Version – da die meisten Programme jQuery auf die eine oder andere Art voraussetzen, empfiehlt sich ihre Verwendung. Im nächsten Schritt wird eine .html-Datei erstellt. Deren Korpus ist in Listing 1 zu sehen.

<html>
<head>
  <title>Hello World</title> 
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <meta name="format-detection" content="telephone=no" />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="jquery.js"></script>
  <script src="jquery.mobile-1.4.2.js"></script> 
  <link rel="stylesheet"  href="jquery.mobile-1.4.2.css"> 
</head>

jQuery Mobile muss in drei Schritten eingebunden werden. Nach dem Laden der „Mutterbibliothek“ folgt zuerst die JavaScript-Datei mit dem Objektcode. Das .css-File enthält diverse „Stilrichtlinien“, die das Aussehen der Applikation beeinflussen. Die von Haus aus voreingestellten Viewport-Einstellungen führen zu unleserlich kleinem Text. Unsere Settings sind praxiserprobt und funktionieren auf den meisten Telefonen ohne allzu große Probleme. Eine auf jQuery Mobile basierende Applikation ist normalerweise in mehrere Seiten aufgeteilt. Diese entstehen durch <div>-Tags, denen die Data Role „page“ zugewiesen wird. Unser Beispiel realisiert zwei Pages, die erste hört auf den Namen „foo“:

<body>
  <div data-role="page" id="foo">

Seiten bestehen normalerweise aus drei Elementen, die wiederum durch <div>-Tags realisiert werden. Der Header dient dabei als Kopfzeile, während der eigentliche Inhalt in den content-Bereich wandert. Eventuelle „Schlussanmerkungen“ sind im Footer bestens aufgehoben. Es ist nicht unbedingt notwendig, Header und Footer anzulegen – jQuery Mobile funktioniert auch dann, wenn Sie „nur“ einen Body mit Inhalten deklarieren:

  <div data-role="header">
    <h1>Seite 1</h1>
  </div><!-- /header -->

Der content-Tag unseres Beispiels enthält eine kleine Besonderheit: jQuery Mobile automatisiert den Wechsel zwischen den einzelnen Seiten ihrer Applikation, wenn sie „passende“ Hyperlinks anlegen (Listing 2).

<p>Das ist die Seite 1.</p>
    <p>Auf die zweite <a href="#bar">Seite springen</a></p>
  </div><!-- /content -->

  <div data-role="footer">
    <h4>Page Footer</h4>
  </div><!-- /footer -->
</div><!-- /page -->

Die Struktur der zweiten Seite ist an die der ersten Seite angelehnt. Der einzige Unterschied besteht darin, dass wir den Hyperlink diesmal auf Page 1 verweisen lassen (Listing 3).

<!-- Hier beginnt Seite 2 -->
<div data-role="page" id="bar>

  <div data-role="header">
    <h1>Seite 2</h1>
  </div><!-- /header -->

  <div data-role="content">
    <p>Das ist die zweite Seite!</p>
    <p<<a href="#foo">Zurück zur Seite 1</a></p>
  </div><!-- /content -->

  <div data-role="footer">
    <h4>Page Footer</h4>
  </div><!-- /footer -->
</div><!-- /page -->
</body> 
</html>

Beim Testen von jQuery-basierten Webseiten ist es hilfreich, einen kleinen Webserver aufzusetzen und die Inhalte so auf die Endgeräte zu bringen. Unter Ubuntu ist dies vergleichsweise simpel, da der Python-Interpreter mit einem Server ausgeliefert wird. Seine Bedienung ist mit python -m SimpleHTTPServer vergleichsweise simpel. Ab diesem Zeitpunkt steht ein HTTP-Server am Port 8000 bereit. Damit ist das Beispiel – fürs Erste – einsatzbereit. Abbildung 1 zeigt, wie die erste Seite auf einem unter Android 4.0 laufenden Smartphone Phablet (Galaxy Note I) im Landscape Mode aussieht.

Abb. 1: jQuery Mobile funktioniert problemlos

Abb. 1: jQuery Mobile funktioniert problemlos

Pop-ups…

Viele Entwickler nutzen jQuery Mobile aufgrund der Vielzahl an enthaltenen Steuerelementen als bessere Widget-Bibliothek. Die Erstellung von Pop-ups und Dialogen ist besonders populär, da HTML5 und Co normalerweise keine Möglichkeit zum Realisieren eines aufpoppenden Fensters mitbringen. jQuery Mobile deckte diesen Anwendungsfall bisher über zwei getrennte Widgets ab: Der Dialog war für „modale“ Klassen zuständig, während sich das Pop-up auch durch einen Klick in den Hintergrund schließen ließ. In der aktuellsten Version des Frameworks wurde die Dialogklasse als „deprecated“ markiert, aufscheinende Fenster sind fortan immer vom Pop-up abgeleitet. Die Verwendung dieser Klasse ist in den beiden Snippets in Listing 4 illustriert.

<div data-role="page" id="foo">
  <div data-role="content">
    <a class="ui-btn ui-corner-all ui-shadow ui-btn-inline" data-transition="pop" data-rel="popup" href="#popup1">Popup</a>
    <a class="ui-btn ui-corner-all ui-shadow ui-btn-inline" data-transition="pop" data-rel="popup" href="#dialog1">Dialog</a>
    <div data-role="popup" id="popup1">
      <div data-role="header" data-theme="a">
        <h1>Willkommen!</h1>
      </div>
      <div role="main" class="ui-content">
        Dies ist ein normales Popup!<br>
        <a href="#" class="ui-btn ui-corner-all ui-shadow ui-btn-inline ui-btn-b" data-rel="back">OK</a>
      </div>
    </div>

    <div data-role="popup" id="dialog1" data-dismissible="false" style="max-width:400px;">
      <div data-role="header" data-theme="a">
        <h1>Dialog!</h1>
      </div>
      <div role="main" class="ui-content">
        Ich lasse mich nur über einen Button schließen!<br>
        <a href="#" class="ui-btn ui-corner-all ui-shadow ui-btn-inline ui-btn-b" data-rel="back">OK!</a>
      </div>
    </div>
  </div><!-- /content -->
</div><!-- /page -->

Die neue Version unserer Page enthält zwei Hyperlinks, die diesmal als Buttons ausgeführt sind. In ihrer Deklaration finden Sie neben der Zuweisung der diversen Stilklassen auch zwei data-Attribute, die das Verhalten und die Beziehung zwischen den Widgets festlegen. Das Übergeben von „pop“ an data-transition sorgt dafür, dass das neue Fenster mit einer Aufpoppanimation am Bildschirm erscheint. Dank der Zuweisung von data-rel weiß das Framework, dass diese Links einen Dialog bzw. ein Pop-up öffnen sollen. Diese Information ist unter anderem für die Verwaltung des Back-Stacks notwendig. Unsere beiden Pop-ups entstehen durch das Setzen der data-role-Eigenschaft. Ein mit data-dismissible=false ausgestattetes Fenster ist modal, lässt sich also nicht durch einen Klick in den Hintergrund schließen. Das Schließen der Dialoge ist in beiden Fällen auch durch einen Link bzw. Button möglich, der als data-rel den Wert „back“ übergeben bekommt.

Die meisten Stilklassen des Frameworks adjustieren ihr Verhalten, wenn sie ein einem Pop-up eingesetzt werden. Unser Beispiel nutzt dies zur Realisierung von professionell aussehenden Kopfzeilen, die den Fenstern einen professionellen Touch geben. Wichtig ist, dass die Definition von Dialogen und/oder Pop-ups immer im Körper einer Page erfolgen muss. Im Mark-up stehende Pop-ups werden von der jQuery-Mobile-Laufzeitumgebung nicht erkannt und führen außerdem zu Grafikfehlern. Damit ist auch unser zweites Programmbeispiel soweit einsatzbereit. In Abbildung 2 sehen Sie das Resultat auf einem unter Android 4.0 laufenden Tablet.

Der Dialog erscheint hervorgehoben an der oberen Kante des Bildschirms

Abb. 2: Der Dialog erscheint hervorgehoben an der oberen Kante des Bildschirms

…ergeben ein Menü

Pop-ups lassen sich nicht nur zur Realisierung von Dialogen verwenden. Entwickler nutzen die Klasse seit Jahren auch als Basis für Pop-up-Menüs – diese anfangs eher als Nebeneffekt angesehene Verwendung wird von Seiten des Herstellers mittlerweile in der offiziellen Dokumentation angeführt und gilt somit als „Design Pattern“ (Listing 5).

<div data-role="popup" id="popupMenue" data-theme="none">
  <div data-role="collapsibleset" data-theme="b" data-content-theme="a" data-collapsed-icon="arrow-r" data-expanded-icon="arrow-d" style="margin:0; width:250px;">
    <div data-role="collapsible" data-inset="false">
      <h2>Gruppe A</h2>
      <ul data-role="listview">
        <li><a href="#" data-rel="back">Verlassen!</a></li>
        <li><a href="#" data-rel="dialog">AB</a></li>
      </ul>
    </div><!-- /collapsible -->
    <div data-role="collapsible" data-inset="false">
      <h2>Gruppe B</h2>
      <ul data-role="listview">
        <li><a href="#" data-rel="dialog">BA</a></li>
      </ul>
    </div><!-- /collapsible -->
    <div data-role="collapsible" data-inset="false">
      <h2>Gruppe C</h2>
      <ul data-role="listview">
        <li><a href="#" data-rel="dialog">CA</a></li>
      </ul>
    </div><!-- /collapsible -->
  </div><!-- /collapsible set -->
</div><!-- /popup -->

Unser Menü besteht aus einem Collapsible-Set, das eine Gruppe von Collapsibles enthält. Der etwas seltsame Name dieser „Klasse“ leitet sich vom englischen Wort für „kollabieren“ ab – es handelt sich dabei um ein Widget, das seine Inhalte „zusammenfaltet“ und so vom Bildschirm entfernt (Abb. 3).

Das mittlere Collapsible ist aufgeklappt, während die beiden äußeren im kompakten Zustand sind

Abb. 3: Das mittlere Collapsible ist aufgeklappt, während die beiden äußeren im kompakten Zustand sind

Die eigentlichen Menüoptionen werden dann in Form von Listen angeliefert. Per data-role wird festgelegt, wie sich die einzelnen Items verhalten. In unserem Beispiel schließt das Item „Verlassen!“ die Liste, während die anderen Items keinen Einfluss auf das Menü nehmen. Aus Platzgründen drucken wir hier die eigentliche Ereignisverarbeitung nicht ab. Die meisten von jQuery Mobile generierten Widgets lassen sich mit normalen Event Handlern nach dem Schema onClick ausstatten.

Gitter herbei

In den Anfangszeigen des Internets waren tabellenbasierte Layouts in Mode – Entwickler nutzten das <table>-Tag zur Realisierung diverser Steuerelementanordnungen. jQuery Mobile begegnet diesem Problem durch das Anbieten eines Grid-Widgets, dessen Verwendung in Listing 6 illustriert ist.

<div class="ui-grid-a">
  <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div> 
  <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div> 
</div><!-- /grid-a -->
<div class="ui-grid-d">
  <div class="ui-block-a"><div class="ui-bar ui-bar-a" style="height:60px">Block A</div></div> 
  <div class="ui-block-b"><div class="ui-bar ui-bar-a" style="height:60px">Block B</div></div> 
  <div class="ui-block-c"><div class="ui-bar ui-bar-a" style="height:60px">Block C</div></div> 
  <div class="ui-block-d"><div class="ui-bar ui-bar-a" style="height:60px">Block D</div></div> 
  <div class="ui-block-e"><div class="ui-bar ui-bar-a" style="height:60px">Block E</div></div> 
</div><!-- /grid-d -->

Unser Beispiel realisiert eine zweireihige und eine fünfreihige Tabelle (Abb. 4). In jQuery Mobile sind Tabellen immer „eine Reihe hoch“, mehrspaltige Versionen entstehen durch das Untereinanderreihen von mehreren <div>-Tags. Die eigentliche Intelligenz findet sich – wie so oft – in der Zuweisung der data-roles. Der Buchstabe hinter ui-grid legt die Art der zu generierenden Tabelle fest. „A“ steht dabei für eine einspaltige Tabelle, „d“ erstellt ein Widget mit fünf Columns. Spalteninhalte werden in Form von <div>-Tags angeliefert, die ihre Position durch Zuweisung einer CSS-Klasse eingeschrieben bekommen. Übrigens: in diesem Listing hat sich kein Tippfehler eingeschlichen. Eine fünfspaltige Tabelle entsteht durch die Erstellung einer vierspaltigen. Dieser wird danach eine fünfte Spalte eingeschrieben, die sich den notwendigen Platz automatisch von der Umgebung „holt“.

Zweizeiliges und fünfzeiliges Grid in friedlicher Zweisamkeit

Abb. 4: Zweizeiliges und fünfzeiliges Grid in friedlicher Zweisamkeit

Mehr Widgets

Zu guter Letzt wollen wir noch einige interessante Widgets in „Kurzform“ vorstellen. Als Erstes wollen wir einen Flipswitch realisieren. Dabei handelt es sich um die von iOS her bekannte Komponente, die dort unter anderem zum Ein- und Ausschalten von WLAN und Bluetooth zum Einsatz kommt. Das dazu notwendige Listing sieht so aus:

<input data-role="flipswitch" name="flip-checkbox-1" id="flip-checkbox-1" type="checkbox">

Die insbesondere in Audio-Apps weit verbreiteten seitlichen Slider lassen sich in jQuery Mobile ebenfalls mit nur einer Zeile Mark-up realisieren. Das Steuerelement ist mittlerweile als „deprecated“ markiert, funktioniert aber nach wie vor problemlos:

<input type="range" name="slider-1" id="slider-1" value="60" min="0" max="100">

Der – wie sein einfacheres Vorbild als „deprecated“ markierte – Rangeslider stellt eine besondere Art des normalen Sliders dar. Er hat „zwei Taps“, die dem Benutzer das Festlegen eines minimalen und eines maximalen Werts erlauben. Sein Code sieht so aus:

<div data-role="rangeslider"> 
  <input name="range-1a" id="range-1a" min="0" max="100" value="0" type="range" /> 
  <input name="range-1b" id="range-1b" min="0" max="100" value="100" type="range" /> 
</div>

Rangeslider entstehen durch das Gruppieren zweier normaler Slider in einem <div>-Tag vom Typ rangeslider – das Framework erkennt diese Konstellation im Rahmen der Analyse des DOMs, und fasst die beiden Widgets wie in Abbildung 5 gezeigt zusammen.

Die beiden Slider wurden zu einem Rangeslider zusammengefasst

Abb. 5: Die beiden Slider wurden zu einem Rangeslider zusammengefasst

Zugriffe auf die im Steuerelement enthaltenen Daten erfolgen normalerweise über eine Akzessorfunktion. Sie bekommt einen Stringparameter übergeben, der die Art der zu erledigenden Aufgabe angibt – die Anzahl der zusätzlich erforderlichen Parameter ist vom übergebenen String abhängig. In unserem Fall ist dies nicht notwendig, da jQuery mit val() einen „Shortcut“ zur Ermittlung der Werte eines Felds anbietet. Seine Verwendung sieht so aus:

function analyzeSlider() 
{ 
  var val1=$('#range-1a').val(); 
  var val2=$('#range-1b').val(); 
  alert("Ranges: " + val1 + " " + val2); 
}

Beachten Sie bitte, dass das direkte Ansprechen der beiden „Subslider“ nur zum Setzen/Auslesen der Werte zulässig ist. Das Aktivieren/Deaktivieren des „gesamten Sliders“ sollte immer über das Mutter-div-Tag und die zugehörige rangeslider()-Funktion erfolgen.

PhoneGap, dozil

Die hier gezeigte Lösung lässt sich mit Fug und Recht als „FAQ“ in Sachen mobiler Frameworks betrachten. PhoneGap-Entwickler betrachten den GUI-Stack des jQuery-Projekts mittlerweile als „ihre Grafik-Engine“ – dementsprechend viele Produkte werden mit der Frameworkkombination ausgeliefert. Ärgerlicherweise emittieren sowohl jQuery Mobile als auch PhoneGap ein eigenes Event, das den Entwickler über die Fertigstellung des Ladeprozesses informiert. Die Abfolge der beiden Ereignisse ist mehr oder weniger zufällig. Im Laufe der Jahre hat sich ein „Standard-Pattern“ herausgearbeitet, das in Listing 7 illustriert wird.

<script type="text/javascript">
  var deviceReadyDeferred = $.Deferred();
  var jqmReadyDeferred = $.Deferred();

  document.addEventListener("deviceReady", deviceReady, false);

  function deviceReady()
  {
    console.log("PG up");
    deviceReadyDeferred.resolve();
  }

  $(document).one("mobileinit", function () 
  {
    console.log("jQM up");
    jqmReadyDeferred.resolve();
  });

  $.when(deviceReadyDeferred, jqmReadyDeferred).then(doWhenBothFrameworksLoaded);

  function doWhenBothFrameworksLoaded() 
  {
    console.log("All up");
  }
</script>
<script src="jquery.mobile-1.3.0.min.js"></script>

Das Pattern nutzt zwei „Oneshot-Timer“, die in der jQuery-Mutterbibliothek enthalten sind. Das Auftreten eines der beiden Ereignisse wird durch den Aufruf von resolve() quittiert, was die Mutex in den fertigen Zustand versetzt. Die when()-Funktion dient als „Sammelstelle“ für die offenen Mutexen. Sie führt die an sie übergebene Playload erst dann aus, wenn alle Oneshots „exekutiert“ wurden. Daraus folgt, dass DoWhenBothFrameworksLoaded() erst dann aufgerufen wird, wenn alle Oneshots „fertig“ sind – zu diesem Zeitpunkt haben beide Frameworks ihre Initialisierungsarbeiten gleichermaßen abgeschlossen.

Mehr jQuery Mobile

Im Laufe der letzten Jahre hat sich ein kleines Ökosystem um den GUI-Stack entwickelt. Wer alle Funktionen von jQuery Mobile „an einem Platz“ sehen möchte, soll seine Erkundungsreise bei der im Ordner /demos/ befindlichen Beispielsammlung beginnen. Öffnen Sie die Datei index.html im Browser ihrer Arbeitsstation, um eine Liste von Steuerelementen samt Beispielen auf den Bildschirm zu bekommen. Informationen über die dahinterstehenden Akzessorfunktionen finden Sie in der API-Übersicht. Diese ist hier aufrufbar. Codiqua hat sich auf das Anbieten eines grafischen Editors für jQuery-Mobile-basierte Applikationen spezialisiert. Hier finden Sie eine Testversion, die Ihnen beim Erstellen von Layouts wertvolle Dienste leistet. Klicken Sie die Benutzerschnittstelle Ihrer Applikation per Drag and Drop zusammen, und klicken Sie daraufhin auf den Code-Button am unteren rechten Bildschirmrand. Das daraufhin erscheinende Fenster enthält den Quellcode Ihrer Seite, den Sie per Copy and Paste in Ihre Applikation übernehmen können (Abb. 6).

Die Demoversion von Codiqua hilft beim Erstellen von Formularlayouts

Abb. 6: Die Demoversion von Codiqua hilft beim Erstellen von Formularlayouts

Weitere Informationen zu den diversen Aspekten von jQuery Mobile finden Sie im Internet und in der Literatur. Die Suche nach „jQuery Mobile“ liefert sowohl bei Amazon als auch bei einer Suchmaschine Ihrer Wahl jede Menge interessanter Resultate.

Fazit

Über das Aussehen von jQuery-Mobile-Applikationen lässt sich vortrefflich streiten: Es steht außer Frage, dass das Design mit Sicherheit nicht jedermanns Geschmack ist. Aufgrund der enormen Verbreitung des Frameworks haben die Widgets mittlerweile einen gewissen „Wiedererkennungswert“ – die User wissen, was sie von den einzelnen Elementen zu erwarten haben. Wer im mobilen Internet surft, trifft über kurz oder lang auf per jQuery generierte Inhalte. Schon allein aus diesem Grund zahlt es sich aus, Grundkenntnisse über die Bibliothek zu besitzen – weitere Information finden sich im Ernstfall fast wie von selbst.

Aufmacherbild: Retriever puppy in colored wig von Shutterstock / Urheberrecht: Chirtsova Natalia

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -