Interaktivität mit Turbo-Frames

Interaktivität (fast) ohne Javascript. Das verspricht das »Turbo»-Projekt. Ein Klick auf einen Buttom ruft in diesem Beispiel eine normale Seite auf, stellt sie aber in einem Modal dar.

Das Hotwire-Projekt verspricht, dynamische Webseiten ohne Javascript möglich zu machen. Drei Einzelprogramme unterstützen Web-Entwickler:

  • Turbo-Drive
    Beschleunigt die Navigation beim klassischen Setup. CSS und Javscript werden wiederverwendet. Bei Seitenaufrufen wird nur noch HTML übertragen.
  • Turbo-Frame
    Erlaubt die Modifikation von Teilen einer Webseite.
  • Turbo-Stream
    Verändert EinzelElemente einer Webseite.

Mit dieser Bibliothek kann beispielsweise ein Button zur Anzeige eines erklärenden Modals programmiert werden. Die Realisierung innerhalb von Bridgetown erfordert (a) eine Ergänzung des HTML-Codes auf der Seite des Buttons, (b) ein Code-Snippet für die index.js.rb und (c) ein angepasstes Layout.

Button mit Turbo-Frame

Der Botton wird mit <sl-button> aus dem Shoelace-Projekt realisiert. Es genügt, beim zugewiesenem Link ein data-turbo-frame Attribut zu definieren und diesen innerhalb eines <turbo-frame>-Tags zu platzieren.

<sl-button type="primary">
  <sl-icon slot='prefix' name="book-half"></sl-icon>
  <turbo-frame>
    <a href="<%= relative_url resource.data.modal %>" data-turbo-frame="1xd">
      So gehts	
    </a>
  </turbo-frame>
</sl-button>

Bei einem Mausklick wird der Link ausgeführt.

Auf der gleichen Seite muss nun das Ziel des Turbo-Frame-Aufrufs definiert werden:

<sl-dialog style="--width: min(90vw,1400px)">
  <turbo-frame id="1xd" target="_top" > 
    <p > <sl-spinner></sl-spinner> </p>
  </turbo-frame>

  <sl-button slot="footer" type="primary" onclick="this.closest('sl-dialog').hide()">
    <sl-icon slot="prefix" name="backspace-fill"></sl-icon>
    Schließen	
  </sl-button>
</sl-dialog>

Das ist zu Beginn eine leere Hülle, die im CSS zudem mit “display: none” versteckt wird. Wichtig ist die Entsprechung der turbo-frame Attribute.
Beide Fragmente werden von einem benannten <div class='modal'> umschlossen.

Zum Abschluß muss dieser Aufruf noch in frontend/javascript/index.js.rb bekannt gemacht werden:
(Ganz ohne Javascript geht es dann doch nicht … )

window.add_event_listener "turbo:load" do
   document.query_selector_all("sl-button").each do |button|
      button.add_event_listener "click" do |e|
         e.current_target.closest('.modal').query_selector('sl-dialog').show()
         if e.target.local_name != "a"
            anchor = e.current_target.query_selector("a")
            anchor.click() if anchor
         end
      end
   end
end

Wenn nun der folgende Button gedrückt wird,

stellt sich die im Frontmatter deklarierte Ressource im Kontext des <sl-dialogs> dar.

Layout für Turbo-Stream

Ruft man eine »normale« Ressource mit diesem Ansatz auf, stellt sich die Seite trotz aller Bemühungen ganz normal dar. Turbo-Stream arbeitet im Fallback-Modus.

Um eine Darstellung in den bereitgestellten Dialog umzuleiten muss das Layout turbostream-fähig gemacht werden.

Hier ein Beispiel für ein modal-taugliches Layout (Skriptsprache: Serbea):

---
layout: post   # fallback
---
<section-wrapper style="padding-top:3rem"><section>
<!--  fall back  --> 
<h3> {{ resource.data.title | smartify  }}</h3>

<turbo-frame id="1xd" target="_top">
	<article class="article">
  	<preamble>{{ markdownify resource.data.preamble }}</preamble>
	  {% if resource.data.image.present? %}
    	{%@  Image file: resource.data.image %}
	  {% end %} 
      {%= yield %}
  </article>
</turbo-frame>
</section></section-wrapper>

Limitationen

Diese Implementation des Modals ist nicht responsiv und führt auf mobilen Endgeräten zu keiner überzeugenden Darstellung. Das Modal wird nicht angemessen skaliert.

Modale Seiten werden von Suchmaschinen in der FallBack-Variante indiziert.