Web

HTMX-Einführung

Dynamisches HTML ohne JavaScript

20.03.2024
Von 
Matthew Tyson ist Java-Entwickler und schreibt unter anderem für unsere US-Schwesterpublikation Infoworld.com.
HTMX könnte die Webentwicklung verändern. Lesen Sie, wie.
HTMX will JavaScript zu Leibe rücken.
HTMX will JavaScript zu Leibe rücken.
Foto: SerFF79 | shutterstock.com

Das HTMX-Projekt existiert schon seit einiger Zeit und erfreut sich seit der Aufnahme ins GitHub-Accelerator-Programm zunehmendem Interesse unter Entwicklern. Dabei könnte das Konzept hinter HTMX die Art und Weise, wie Web-Frontends künftig funktionieren, wesentlich beeinflussen.

In diesem Artikel werfen wir einen Blick darauf, wie HTMX verwendet wird - und was den Ansatz so überzeugend macht.

Was ist HTMX?

Die Grundidee von HTMX-Schöpfer Carson Gross: Häufige Anwendungsfälle, die Standard-JavaScript- und -HTML-Interaktionen erfordern, in eine HTML-Syntax umzuwandeln, ohne auf JavaScript zurückzugreifen. Viele Interaktionen werden mit HTMX also deklarativ.

Diese HTMX-Demo bietet einen kleinen Vorgeschmack. Im Wesentlichen klicken Sie auf eine Schaltfläche, um die Felder des User-Objekts zu bearbeiten. Die Daten werden dabei tatsächlich in einen Backend-Endpoint übertragen - wie im folgenden Screenshot zu sehen. Beachten Sie dabei die Netzwerkinteraktion im unteren Teil des Bilds.

Ein Blick auf die HTMX-Demo.
Ein Blick auf die HTMX-Demo.
Foto: Matthew Tyson | IDG

Normalerweise würde all das (in irgendeiner Form) JavaScript erfordern, ganz unabhängig davon, welches Framework zum Einsatz kommt. HTMX transformiert die Interaktion in zwei Markup-Blöcke - einen für die Display-UI und einen für die Edit-UI, wie im Folgenden zu sehen ist.

Listing 1 - Das User-Update in HTMX

<div hx-target="this" hx-swap="outerHTML">

<div><label>First Name</label>: Joe</div>

<div><label>Last Name</label>: Blow</div>

<div><label>Email</label>: joe@blow.com</div>

<button hx-get="/contact/1/edit" class="btn btn-primary">

Click To Edit

</button>

</div>

<!-- The edit: -->

<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">

<div>

<label>First Name</label>

<input type="text" name="firstName" value="Joe">

</div>

<div class="form-group">

<label>Last Name</label>

<input type="text" name="lastName" value="Blow">

</div>

<div class="form-group">

<label>Email Address</label>

<input type="email" name="email" value="joe@blow.com">

</div>

<button class="btn">Submit</button>

<button class="btn" hx-get="/contact/1">Cancel</button>

</form>

Wenn Sie sich das Markup in Listing 1 ansehen, können Sie leicht erkennen, was passiert: Das hx-swap-Attribut liefert das HTML für das div, bevor es bearbeitet wird. Das outerHTML teilt dem Framework mit, wie es sich zum dynamischen Inhalt darin verhält. Die editierbare Version ist ein Formularelement, das die Property x-put enthält, die wiederum die PUT-HTML-Methode und den zu verwendenden Endpunkt identifiziert.

Die Frage, die sich nun stellt: Wie erreicht HTMX diesen "Swap" und das anschließende PUT ohne JavaScript? Die Antwort ist simpel: Es nutzt das serverseitige HTML-Rendering für das Edit-Markup und abstrahiert das Formular-Marshaling in das Framework. JavaScript werkelt also immer noch hinter den Kulissen. Das Ergebnis ist im Wesentlichen eine granularere HTML-Syntax, die nur Segmente anstelle ganzer Seiten lädt und Ajax-Requests stellen kann.

Ein interessantes Beispiel für das DRY-Prinzip in Aktion. Selbst im Fall von React gibt es eine gewisse Menge an Boilerplate-Code, der Informationen von einem Formular zum nächsten schiebt. Das kann zwar auch HTMX nicht vollständig eliminieren - aber es verlagert die Arbeit auf den Server.

Serverseitiges HTMX

Für serverseitige Technologien, die HTMX verwenden, gibt es diverse Beispiele. Das liegt in erster Linie daran, dass HTMX laut seinem Erfinder Gross "Backend-agnostisch" ist. Es ist also egal, welches Backend Sie verwenden, solange es HTML erzeugt. Um ein Gefühl dafür zu bekommen, wie HTMX funktioniert, betrachten wir nun ein klassisches TODO-Beispiel, das Express zusammen mit der Pug-HTML-Templating-Engine verwendet.

Zu Beginn werden die vorhandenen ToDo-Items über Express mit folgendem Befehl ausgegeben:

res.render('index', { todos: filteredTodos, filter, itemsLeft: getItemsLeft() });

Dieser Befehl nutzt die In-Memory-To-Do-Sammlung und rendert sie mit einem Pug-Template. Letzteres weist das typische Format auf - mit dem Unterschied, dass es die speziellen hx-Properties enthält, die die HTMX-Interaktionen steuern. Listing 2 zeigt das Formular, um neue To-Dos zu posten.

Listing 2 - POST mit HTMX-Properties

form(hx-post="/todos", hx-target="#todo-list", hx-swap="afterbegin", _="on htmx:afterOnLoad set #txtTodo.value to ''")

input#txtTodo.new-todo(name="todo",placeholder='What needs to be done?', autofocus='')

Hier können Sie sehen, wie das Attribut afterbegin funktioniert, um den neuen Inhalt an die richtige Stelle in der Liste zu setzen. Das on htmx-Scripting ist ein Beispiel für Hyperscript, eine Art vereinfachte Skriptsprache. Sie wird oft zusammen mit HTMX verwendet, ist aber nicht erforderlich, um es zu verwenden. Im Wesentlichen wird on htmx hier verwendet, um den Wert des Eingabeformulars zu setzen, nachdem das neue To-Do erstellt worden ist. Das nachfolgende Listing 3 zeigt das Pug-Template für den To-Do-Edit.

Listing 3 - Server-seitiges Template in Pug bearbeiten

form(hx-post="/todos/update/" + todo.id)

input.edit(type="text", name="name",value=todo.name)

Das Markup verwendet hier die Eigenschaft hx-post, um anzugeben, wohin das JSON für das bearbeitete To-Do zu senden ist. Aus diesen Beispielen lässt sich ableiten: Der Server ist dafür verantwortlich, HTML (verziert mit HTMX-Tags) in der richtigen Größe bereitzustellen, um die verschiedenen Teile des Bildschirms zu füllen, die das Frontend für seine Interaktionen benötigt. Der HTMX-Client platziert sie dort, wo sie auf der Grundlage der Eigenschaften hingehören und kümmert sich zudem darum, die entsprechenden Verbrauchsdaten zu senden.

Die Endpunkte, die dafür zuständig sind, die Daten zu empfangen, können wie typische Endpoints arbeiten - mit dem Unterschied, dass die Antwort das nötige HTMX beinhaltet. In Listing 4 sehen Sie beispielsweise, wie der Express-Server die POST-Anfrage behandelt, um ein neues To-Do zu erstellen.

Listing 4 - To-do-Kreation

app.post('/todos', (req, res) => {

const { todo } = req.body;

const newTodo = {

id: uuid(),

name: todo,

done: false

};

todos.push(newTodo);

let template = pug.compileFile('views/includes/todo-item.pug');

let markup = template({ todo: newTodo});

template = pug.compileFile('views/includes/item-count.pug');

markup += template({ itemsLeft: getItemsLeft()});

res.send(markup);

});

Hierbei handelt es sich um einen typischen POST-Body-Handler, der die Werte aus den Formulardaten übernimmt und damit ein neues Business Object erstellt (newTodo). Anschließend werden die Werte verwendet, um das Pug-Template zu füllen und an den Client zurückzusenden, wo sie der Todo-Liste auf dem Frontend hinzugefügt wird.

Client-seitiges Templating mit HTMX

Eine Variante dieses Musters, die von HTMX unterstützt wird, ist die Verwendung von Client-seitigen Templates. Dabei handelt es sich um eine Schicht, die auf dem Client läuft, JSON vom Server entgegennimmt und dort die Übersetzung des Markups vornimmt. Laut Carson Gross ist es mit Client-seitigen Templates möglich, einen RESTful-Service mit JSON zu nutzen.

Die umgekehrte Frage lautet also: Wie können wir JSON an den Server übermitteln, ohne die Standardformularkodierung zu verwenden? Auch hierfür gibt es eine Erweiterung, nämlich JSON-ENC.

HTMX als neuer Interaktionsansatz fürs Web

HTMX wird als wirklich ausgereiftes Projekt wahrscheinlich nicht mehr genau so funktionieren wie heute - hat aber bereits positiven Einfluss.

Am überzeugendsten ist die Idee, die gehäuften Ajax-artigen Requests, die normalerweise fetch() oder etwas Ähnliches nach sich ziehen würden, mit nur einer HTML-Eigenschaft zu behandeln. Das ist einfach einfacher, sauberer und sorgt dafür, dass alles an einem Ort bleibt. Es ist offensichtlich, was das Markup tut.

Die server-seitige Markup-Generierung kann man zwiespältig sehen: Entwickler sind daran gewöhnt, zu diesem Zweck mit JSON zu arbeiten - Markup einzufügen, bedeutet insofern vor allem einen weiteren Schritt bei der Client-Erstellung. Geht es um die Option, Client-seitige Templates zu erstellen, bei denen der Server als JSON-Emitter fungiert, stellt sich vor allem eine Frage: Würde das die Gesamtkomplexität in einem großangelegten Softwareprojekt reduzieren?

HTMX will die Dinge vereinfachen, indem sie uns zu Hypertext als Zustandsmechanismus für Webanwendungen zurückführt. Dieses Beispiel zeigt die Idee in der Praxis. JSON als Protokoll zu verwenden, bedeutet, dass die Clients intelligenter und komplexer werden und die Architektur weniger selbstbeschreibend ist.

Das könnte alles funktionieren: Wenn wir die inhärente Komplexität vermeiden würden, indem wir die zugrundeliegende Sprache, HTML, so erweitern, dass sie modernen Anforderungen wie Ajax gerecht wird, könnten wir zu einer einfacheren Zeit zurückkehren. Das Markup wäre wieder die zentrale Datenbeschreibung und würde ausreichen, um sowohl die Benutzeroberfläche als auch die Daten zu beschreiben.

Dieser Beitrag basiert auf einem Artikel unserer US-Schwesterpublikation Infoworld.