Hugo: JavaSript nur bei Bedarf laden (.Scratch und Resources nutzen)
Auf einer Hugo-Seite werden Bilder eingesetzt, die die Venobox als PopUp-Feature verwenden. Die benötigt bei mir zwei zu ladende JavaScript-Dateien. Für das <FIGURE><A><IMG>
-HTML-Gerüst nutze ich einen eigenen Hugo-Shortcode. Ziel ist, dass o.g. Venobox-Skripte nur in Seiten geladen werden, die diesen Shortcode verwenden und, wenn mehrfach, so, dass pro Seite nur ein <SCRIPT>
-Tag eingesetzt wird.
Hinweis
Da die Hugo-Dokumentation und das -Forum (gespickt mit Verlinkungen auf die Dokumentation, von der man gerade verwirrt kommt) mir nur gelegentlich hilft, auf passende Lösungen für speziellere, aktuelle Probleme zu kommen, sind meine selbst gebastelten, teils aus der Verzweiflung geborenen Wege sicherlich zu hinterfragen. Auf Performanceunterschiede beim Rendern im Sekunden-Bereich nehme ich wenig Rücksicht. Sie betreffen ja nicht die fertige Seite.
Ich schreibe extra "Resourcen" und nicht deutsch-korrekt-penibel "Ressourcen"; in Anlehnung an den Hugo-typischen Begriff resources
, ein wichtiges Thema bei den Hugos.
Venobox-Skripte in geeignetes Verzeichnis legen
Im Stammverzeichnis meiner Hugo-Installation habe ich einen Ordner /assets/.
Das ist ein optionales Verzeichnis. Die Dokumentation sagt dazu; frei übersetzt und ergänzt:
Ordner/assets/
enthält die Dateien bzw. Resourcen, die von Hugo-Pipes verarbeitet werden sollen. Nur die Dateien, deren.Permalink
oder.RelPermalink
im Code verwendet werden, werden der gerenderten Seite hinzugefügt, in diese hineinkopiert, damit sie auch im Online-Betrieb verfügbar sind.
Es gibt weitere Verzeichnisse, die als Resourcen-Quellen verwendet werden könnten. Ich bevorzuge einfache Strukturen, bleibe damit bei /assets/
.
In diesen Ordner vefrachte ich meine beiden Dateien. Es können auch weitere *.js
-Dateien sein. Alle werden später "mitgenommen". Sollte die Reihenfolge der Einbindung wichtig sein: Ich habe bisher nicht ausprobiert, ob Nummerierung der Dateinamen eine adäquate Lösung ist.
/assets/js/venobox/venobox-init.js
: Mein eigener Initialisierungs-Code für die PopUps./assets/js/venobox/venobox.min.js
: Die Coredatei von Venobox. Man kann auch die unminifizierte nehmen.
Wichtig ist hier, dass beide im selben Ordner liegen und ein eigener Ordner nur für diese verwendet wird. Alles andere macht im Moment die Sache nur aufwändiger.
Im Hugo-Shortcode das Laden der Dateien anfordern
Bei mir ist das die /layouts/shortcodes/ghsvs/figureVenobox.html
.
Nur der Vollständigkeit halber: In *.md
-Content-Files wird der Shortcode so aufgerufen, um ein Bild mit Venoboxfeature einzufügen:
{{< ghsvs/figureVenobox src="/bild.jpg" >}}
In den Shortcode setze ich eine selbst "erfundene" Zeile ein.
{{ $.Page.Scratch.SetInMap "SCRATCH_JS" "venobox" true }}
Wie immer Professoren dieses .Scratch
-Dingens fachsprachlich auch nennen, für mich ist es in diesem Anwendungsfall ein Container, in dem man Daten für eine individuelle, gerade abzuarbeitende Seite ($.Page
) beim Rendern zwischenspeichern kann, um sie zu einem späteren Zeitpunkt auszulesen und zu verarbeiten; beispielsweise in einem Hugotemplate.
Durch die Verwendung von .SetInMap
sorge ich dafür, dass die Zeile beliebig oft durchlaufen werden kann (weil ich ja öfters mehrere Bilder pro "Page" drinnen habe) ohne, dass mein selbst erfundenes ""Array"" (vielleicht ist es ja auch eins) SCRATCH_JS
das Schlüssel-Wert-Paar venobox:true
mehrfach zwischenspeichert.
Mein SCRATCH_JS
kann natürlich für weitere, zusätzliche Schlüssel-Wert-Paare verwendet werden, falls mal nötig.
Der Schlüssel venobox
ist zugleich der unmittelbare Ordner-Name, in den ich meine JavaScript-Dateien reinkopiert habe (s.o.). Der Wert true
ist übrigens pupsegal. Ich hätte auch false
oder "kokolores"
setzen können.
Jedenfalls hat der Shortcode auf diese Weise vermerkt, dass er verwendet wurde und das JavaScript, in o.g. Ordner befindlich, geladen haben möchte. Bei anderen Content-Seiten, die den Shortcode nicht aufrufen, nicht.
Abgreifen und Aufarbeiten der gespeicherten .Scratch
-Daten
Ich mache das in einem block
im Hugo-Basistemplate baseof.html
. Ob das elegant ist, so wie der Code selbst, oder eigentlich woanders hingehört, sei dahingestellt. Ich mache immer erst mal, um auf der Live-Seite das zu sehen, was ich sehen will, und verbessere/optimiere erst später, wenn ich dann vielleicht mal schlauer bin als heute.
{{- define "scratch_js" -}}
{{- /*
Beispiel /site/assets/venobox/.*.js.
Shortcode hat Zeile $.Page.Scratch.SetInMap "SCRATCH_JS" "venobox" true.
Folgend fasst alle darin enthaltenen *.js zusammen.
Lädt dann online src="/assets/js/scratch_js/venobox.min.js".
*/ -}}
{{- range $folder,$dummy := $.Page.Scratch.Get "SCRATCH_JS" -}}
{{- $SCRATCH_JS := resources.Match (printf "%s/%s/%s" "js" $folder "*.js") -}}
{{- if $SCRATCH_JS -}}
{{- $targetPath := printf "%s%s%s" "/assets/js/scratch_js/" $folder ".js" -}}
{{- $targetJS := $SCRATCH_JS | resources.Concat $targetPath | resources.Minify -}}
<script src="/{{ $targetJS.Permalink | relURL }}"></script>
{{- end -}}
{{- end -}}
{{- end -}}
Zeilen 1 und 16 definieren einen Hugo-block
, den ich, weil's mir so gefällt, scratch_js
genannt habe. Der dazwischen enthaltene Code (Zeilen 8 bis 15) kann aber auch anderweitig eingesetzt werden, also ohne diese block
-Definition zu nutzen.
Zeile 8 liest die hier und da zwischengespeicherten "SCRATCH_JS"
-Daten schleifenweise aus (range
). So eine Art foreach
. Der Schlüssel, in unserem Beispiel venobox
, wird in der Variable $folder
abgelegt.
Zeile 9 versucht nun, die so genannten Resourcen, nämlich alle *.js
-Dateien, die sich im Ordner /assets/js/venobox/
befinden, aufzusammeln. Dass automatisch im assets
-Verzeichnis, weiß Hugo selbst. Nur das js/venobox
muss ich angeben.
Diese Sammlung lege ich in Variable $SCRATCH_JS
ab, die ich so genannt habe, weil's ...na?... mir so gefällt. In Zeile 10 muss unbedingt geprüft werden, ob sie was enthält, sonst hängt sich der Hugo-Renderer später auf.
In Zeile 11 definiere ich die Zieldatei $targetPath
. Achtung! Nicht verwechseln! Bei mir beginnt der Zielpfad nur zufällig auch mit /assets/js/
. Das ist aber ein Ordner in der später gerenderten Online-Seite. Meine Zieldatei wird also sein /assets/js/scratch_js/venobox.min.js
. Warum jetzt .min
? Steht da doch gar nicht! Weil ich die Datei später noch minifiziere. Hugo weiß dann selbst, dass da ein .min
reingehört. Muss uns nicht tangieren.
Zeile 12 ist nun die Zauberzeile, in der die oben erwähnten "Pipes" zur Anwendung kommen (die senkrechten Striche |
), durch die die gerade gesammelten Resourcen durchmarschieren.
Die Resourcen Sammlung $SCRATCH_JS
in Pipe 1 schicken, wo sie zur Zieldatei "concated" (zusammengefasst) wird. Alle gefundenen Quelldateien in eine Zieldatei.
Dieses konkatierte Ergebnis noch in Pipe 2 minifizieren. Muss man nicht, darf man aber.
Das Gesamtergebnis wird in meiner Variablen $targetJS
abgelegt.
Passiert ist bis hierhin noch nichts Sichtbares. Man findet noch keine Datei /assets/js/scratch_js/venobox.min.js
in der gerenderten Seite (außer sie liegt da noch aus einem früheren Lauf drinnen).
Erinnern wir uns an obiges Zitat:
...Nur die Dateien, deren.Permalink
oder.RelPermalink
im Code verwendet werden, werden der gerenderten Seite hinzugefügt, in diese hineinkopiert...
Und das tue ich in Zeile 13. Bei der Gelegenheit lasse ich auch gleich einen <SCRIPT>
-Tag erzeugen, der ja sowieso in die Seite reinsoll. Könnte ich aber auch weglassen, wenn ich die Datei nur speichern wollte:
{{ $blubber := $targetJS.Permalink }}
ODER , aber von mir ungetestet und nur gerüchteweise
{{ $targetJS
.Publish }}
Nachtrag: .Publish
funktioniert ebenfalls. Die Datei wird gespeichert.
Der Vollständigkeit halber
Meinen oben definierten block
namens scratch_js
rufe ich z.B. vor dem schließenden <BODY>
im Template auf.
</div>
{{ end }}
</main>
{{ partial "divEnd" . }}
{{ partial "back-to-top.html" . }}
{{ block "copyright" . }}{{ end }}
{{ block "scratch_js" . }}{{ end }}
</body>
</html>
So finde ich dann in allen fertig gerenderten html-Dateien, die Venobox verwenden, am Ende
...
<script src=/assets/js/scratch_js/venobox.min.js></script>
</body>
</html>