Interaktivität mit Alpine.js
Auf dieser Seite
Interaktivität mit Alpine.js
Jede von BxSites gebaute Seite lädt bereits Alpine.js
- es treibt den eingebauten Dunkelmodus-Umschalter und das
Sprachdropdown in jedem integrierten Theme an. Genau dieselbe
Alpine-Instanz steht auch deinem eigenen Seiteninhalt kostenlos zur
VerfĂĽgung: keine
bxsites.yaml-Einstellung zum Umlegen, keinextraJs-Eintrag hinzuzufügen, kein zusätzliches<script>-Tag in deinem Markdown zu schreiben.
Da rohes block-level HTML unverändert durchgereicht
wird in deinem Markdown,
kannst du Alpines x-data/x-show/@click/etc.-Attribute direkt auf
jeden HTML-Block legen, und es funktioniert einfach.
Bevor du zu Alpine greifst
Die meisten "interaktiven" BedĂĽrfnisse haben bereits einen zweckgebauten Direktiv-Block, fĂĽr den du selbst kein JS schreiben musst
-
greif zuerst zu diesen:
-
Ein einklappbarer Bereich → Expandable oder eine einklappbare Admonition
-
Gruppierter alternativer Inhalt hinter klickbaren Tabs → Content-Tabs
-
Eine nummerierte Schritt-für-Schritt-Anleitung → Stepper
-
Ein gestylter Call-to-Action-Link → Buttons (der Kopieren-Button unten ist ein anderer Fall - er hat überhaupt kein
href, nur clientseitiges Verhalten - genau dafĂĽr ist Alpine da)
Alpine ist fĂĽr den interaktiven Inhalt gedacht, den diese nicht abdecken - alles mit eigenem clientseitigem Zustand.
Ein Kopieren-in-die-Zwischenablage-Button
::: button rendert immer nur einen echten
Link (oder einen inaktiven Platzhalter) - wie GitBooks eigener Button hat
es keine Vorstellung davon, beim Klick beliebiges JS auszufĂĽhren. FĂĽr
einen Button, der stattdessen tatsächlich etwas tut, statt irgendwohin
zu navigieren, hänge stattdessen dessen
bxsites-button/bxsites-button--*-Klassen an ein schlichtes
HTML-<button> - derselbe Look, in jedem integrierten Theme gestylt, nur
mit Alpine statt mit einem href verdrahtet. Ein häufiger Fall: ein
Button neben einem Installationsbefehl, der ihn kopiert und die Kopie
bestätigt:
<div x-data="{ copied: false }">
<button type="button" class="bxsites-button bxsites-button--secondary bxsites-button--small"
@click="navigator.clipboard.writeText( 'box install bx-sites' ); copied = true; setTimeout( () => copied = false, 1500 )">
<span x-show="!copied">Copy install command</span>
<span x-show="copied" x-cloak>Copied!</span>
</button>
</div>
Ein Live-Filter
Eine Liste clientseitig filtern, ohne Server-Roundtrip:
<div x-data="{ query: '' }">
<input type="text" x-model="query" placeholder="Filter providers...">
<ul>
<li x-show="'local'.includes( query.toLowerCase() )">local (static index, no server)</li>
<li x-show="'algolia'.includes( query.toLowerCase() )">algolia (hosted DocSearch)</li>
<li x-show="'pagefind'.includes( query.toLowerCase() )">pagefind (indexed at build time)</li>
</ul>
</div>
x-model bindet den Wert des Eingabefelds an den Alpine-Zustand; das
x-show jedes <li> wertet bei jedem Tastendruck neu aus.
Eine sortierbare, filterbare Tabelle
Eine native Pipe-Tabelle ist statisch, sobald sie
gebaut ist - für eine, die ein Leser tatsächlich clientseitig sortieren
und filtern kann (das Nächste, was es hier zu GitBooks
Tabellen-Suche/-Sortierung gibt), lass stattdessen Alpine die Zeilen
besitzen: leg die Daten in x-data ab und rendere sie mit x-for, statt
die | Feature | Status |-Pipe-Syntax zu schreiben:
<div x-data="{
query: '',
sortKey: 'name',
sortAsc: true,
rows: [
{ name: 'Bootstrap', type: 'Components', stars: 4 },
{ name: 'GitBook', type: 'SaaS', stars: 5 },
{ name: 'Docusaurus', type: 'React', stars: 4 },
{ name: 'VuePress', type: 'Vue', stars: 3 }
],
sortBy(key) {
this.sortAsc = this.sortKey === key ? !this.sortAsc : true
this.sortKey = key
},
get sorted() {
return [...this.rows]
.filter(r => r.name.toLowerCase().includes(this.query.toLowerCase()))
.sort((a, b) => {
const dir = this.sortAsc ? 1 : -1
return a[this.sortKey] > b[this.sortKey] ? dir : a[this.sortKey] < b[this.sortKey] ? -dir : 0
})
}
}">
<input type="text" x-model="query" placeholder="Filter by name...">
<table class="table">
<thead>
<tr>
<th @click="sortBy('name')" style="cursor:pointer">Name</th>
<th @click="sortBy('type')" style="cursor:pointer">Type</th>
<th @click="sortBy('stars')" style="cursor:pointer">Stars</th>
</tr>
</thead>
<tbody>
<template x-for="row in sorted" :key="row.name">
<tr>
<td x-text="row.name"></td>
<td x-text="row.type"></td>
<td x-text="row.stars"></td>
</tr>
</template>
</tbody>
</table>
</div>
Was so gerendert wird (tippe in das Feld, klicke auf eine SpaltenĂĽberschrift):
| Name | Type | Stars |
|---|---|---|
rows ist ein einfaches, direkt in die Seite gebackenes JS-Array - gut
genug für die Art kleiner Referenztabelle, die Docs tatsächlich haben.
sorted ist ein Alpine-getter, filtert und sortiert also bei jedem
Tastendruck/Klick neu, ganz ohne zusätzliche Verdrahtung; sortBy()
kehrt die Richtung bei einem zweiten Klick auf dieselbe Spalte um. Das
<table> hier ist ein von Hand geschriebenes, echtes <table>-Tag (es
gibt keine Pipe-Tabellen-Syntax, um Zeilen direkt an Alpine zu ĂĽbergeben),
also wird es trotzdem automatisch in .bxsites-table-wrap eingepackt und
erhält automatisch die Behandlung für responsives Scrollen und eine
fixierte Kopfzeile,
genau wie jede Tabelle, die bx-markdown selbst rendert.
x-data-Grundlagen, falls du neu bei Alpine bist
x-data deklariert den eigenen reaktiven Zustand eines Bereichs als
schlichtes JS-Objekt; alles innerhalb dieses Elements kann ihn lesen/
schreiben, und x-show/x-text/x-model/@click (Kurzform fĂĽr
x-on:click) reagieren alle auf seine Änderung:
<div x-data="{ count: 0 }">
<button type="button" @click="count++">Clicked <span x-text="count"></span> times</button>
</div>
Siehe Alpines eigene Dokumentation fĂĽr
die vollständige Liste der Direktiven (x-if, x-for, x-transition
und mehr).
Wissenswertes
- Es ist zentral, nicht optional. Das Theme-Chrome (Dunkelmodus,
Sprachumschalter) hängt von Alpine ab, es lässt sich also nicht wie
mermaid/mathinbxsites.yamlausschalten. - Version. Aktuell
alpinejs@3.14.1, mit diesem Modul vendoriert und ausgeliefert vonsite/assets/vendor/alpine/- kein CDN beteiligt. Sieh im eigenenlayout.bxmeines Themes nach, welches genau geladen wird, wenn du das exakt wissen musst. - Strikte CSP. Alpines Standard-Build wertet die JS-AusdrĂĽcke
innerhalb von
x-data/@clicketc. direkt aus, wasunsafe-evalunter einer strikten Content-Security-Policy braucht. Wenn dein Deployment das nicht erlauben kann, verlass dich in deinem Seiteninhalt nicht auf Alpine. - Leichtgewichtig halten. Eine Docs-Seite sollte schnell und einfach bleiben - kleine, in sich geschlossene Widgets (ein Kopieren-Button, ein Filter, ein Umschalter) passen gut; eine vollständige clientseitige App ist nicht der Zweck davon.