Vor mehr als 9 Jahren schrieb ich einen Blog-Beitrag darüber, wie man die „JavaScript Performance bei Scroll-Event-Handlern verbessern“ kann. Damals haben viele ein Plugin wie „WP-Smooth-Scroll“ verwendet (das es immer noch gibt, aber das seit 11 Jahren nicht mehr aktualisiert wurde). Smooth Scrolling wurde oft in Kombination mit einem „Scroll to top“ Button verwendet. Der Scroll-Effekt wurde mit der Funktion scrollTop() von jQuery erzeugt. Heutzutage wird jQuery nicht mehr so häufig verwendet. Aber das Scrollen mit JavaScript gibt es immer noch.
Scrollen einer Seite mit JavaScript
Neben den Schaltflächen zum Scrollen nach oben kann eine Website auch das Smooth Scrolling auch für Ankerlinks verwenden. Wenn ihr es in Aktion sehen wollt, brauchen ihr nur in der Datei wp-includes/js/admin-bar.js im WordPress Core nachsehen, wo ihr diese Funktion vorfindet:
/**
* Scrolls to the top of the page.
*
* @since 3.4.0
*
* @param {Event} event The Click event.
*
* @return {void}
*/
function scrollToTop( event ) {
// Only scroll when clicking on the wpadminbar, not on menus or submenus.
if (
event.target &&
event.target.id !== 'wpadminbar' &&
event.target.id !== 'wp-admin-bar-top-secondary'
) {
return;
}
try {
window.scrollTo( {
top: -32,
left: 0,
behavior: 'smooth'
} );
} catch ( er ) {
window.scrollTo( 0, -32 );
}
}
Wie ihr im Kommentar ersehen könnt, gibt es diese Funktion schon seit WordPress 3.4, das im Juni 2012 veröffentlicht wurde! Das behavior: "smooth" gibt es in Firefox erst seit 2015 und in Chrome seit 2017. Aber auch heute noch können ihr auf die „Admin Bar“ klicken (auf irgendetwas anderes als einen Link) und die Website scrollt „smooth“ nach oben.
Das Problem mit der Methode
Wie wir im obigen Code sehen können, übergibt man der scrollTo() Funktion ein Objekt mit der genauen Scroll-Position im Viewport. Für die WordPress „Admin Bar“ ist das einfach, da sie am oberen Rand der Seite klebt. Wenn wir aber über einen Ankerlink zu einem anderen Element auf der Seite scrollen wollen, müssen wir die Position berechnen. Erinnert ihr sich an unseren letzten Blog-Beitrag zum Thema CSS, „Wie man Fehler mit einem „Sticky Header“ behebt„? Wenn wir sicherstellen wollen, dass die Scroll-Position korrekt ist, müssen wir auch das scroll-margin-top Offset berücksichtigen.
Ein alternativer JavaScript-Ansatz
Es gibt noch eine andere Scroll-Funktion, die ebenfalls schon seit vielen Jahren existiert: die Funktion scrollIntoView(). Bei dieser Funktion müssen wir die Position nicht berechnen. Wir wählen einfach das Element aus, zu dem wir scrollen wollen, und wenden die Funktion darauf an:
const element = document.getElementById("some-anchor");
element.scrollIntoView();
Das Tolle an dieser Funktion ist, dass man ihr sogar mitteilen kann, wo das Element platziert werden soll. Und sie berücksichtigt scroll-margin-top oder ähnliche Eigenschaften. Auf der Dokumentationsseite gibt es noch einige weitere Beispiele. Eine dieser anderen CSS-Eigenschaften ist diejenige, über die ich in diesem Beitrag schreiben möchte.
Das CSS-Scroll-Verhalten
Auch ohne JavaScript ihr „Smooth Scrolling“ erreichen und ihr braucht dazu nur CSS. Fügen Sie einfach Folgendes zu eurer Seite hinzu:
html {
scroll-behavior: smooth;
}
Wenn ihr nun auf einen beliebigen Ankerlink klickt, wird die Seite „smoot“ zu diesem Element gescrollt. Und da der Browser genau weiß, wie er das machen soll, erhalten wir immer die perfekte Scroll-Position für dieses Element.
Fazit
Natürlich ihr eine Funktion mit 20 Zeilen wie in WordPress Core verwenden, um einen Smooth Scrolling umzusetzen. Aber ihr könnten auch nur eine CSS-Eigenschaft und normale Ankerlinks verwenden, um das gleiche Ergebnis zu erzielen. Und ich habe euch noch nicht einmal von all den Problemen mit JavaScript-Scrolling erzählt, wie z. B. manuelles Scrollen, während das Smooth Scrolling noch animiert ist, falsche Positionsberechnung aufgrund von Lazy Loading usw. Ich hoffe, das reicht, um Sie vorerst zu überzeugen. 😉
Vielleicht fragt ihr ich euch jetzt: Was?! Das war auch meine erste Reaktion, als ich von Labeled Statements (Anweisungen mit Bezeichnung) gehört habe. Was also sind sie und wann können sie verwendet werden?
Sind es goto Anweisungen?
Viele Programmiersprachen haben goto Anweisungen. Einige Programmiersprachen haben sie sogar erst in späteren Versionen eingeführt, so wie PHP in Version 5.3. Und so würden sie in PHP aussehen:
$array = [
'one' => [ 1, 2, 3 ],
'two' => [ 4, 5, 6 ],
];
foreach ( $array as $key => $numbers ) {
foreach ( $numbers as $number ) {
if ( $number === 4 ) {
goto fourFound;
}
}
}
echo "There is no 4 in the array";
goto end;
fourFound:
printf( "There is a 4 in array '%s'", $key );
end:
In diesem Beispiel haben wir zwei foreach-Schleifen verwenden. Der Code versucht, in einem zweidimensionalen Array eine 4 zu finden. Sobald eine 4 gefunden wird, können wir die Schleifen beenden und das Ergebnis ausgeben. Um die Schleife(n) zu verlassen, verwendet dieser Code eine goto Anweisung. Dazu springt es in Zeile 17 zum Label fourFound, wo wir dann das Ergebnis ausgeben. Es gibt auch eine zweite goto Anweisung in Zeile 15, die zum end Label springen würde. In jedem Fall würde nur eine echo/printf Funktion aufgerufen werden.
Mit etwas Erfahrung in der Entwicklung von gutem Code, erkennt man hier mehrere Möglichkeiten, um diesen Code zu verbessern. Ich hoffe aber, dass er trotzdem demonstrieren kann, wie ein goto in PHP (und vielen anderen Sprachen) funktioniert. Aber was sind nun Labeled Statements in JavaScript? Sie ähneln den hier gezeigten Labels in PHP, aber es sind keine goto Anweisung und diese Labels können nur in Kombination mit dem break oder continue Keyword verwendet werden.
Wie könnt continue und break in eurem Code verwenden?
Wenn ihr eine Schleife habt, dann könnt ihr aus der Schleife ausbrechen, indem ihr die break Anweisung wie folgt verwendet:
const numbers = [1, 2, 3, 4, 5, 6];
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] === 4) {
console.log(`Found 4 at index ${i}`);
break;
}
}
Dieser Code würde das numbers Array durchlaufen, aber sobald die 4 gefunden wurde, würde der Code aus dem Array „ausbrechen“ und sich die anderen Werte nicht weiter ansehen.
Die continue Anweisung kann verwendet werden, um die aktuelle Iteration zu beenden und mit der nächsten zu beginnen. Wie in diesem modifizierten Beispiel:
for ( let i = 0; i < numbers.length; i++ ) {
if ( numbers[i] % 2 !== 0 ) {
continue;
}
if ( numbers[i] === 4 ) {
console.log( `Found 4 at index ${i}` );
break;
}
}
In Zeile 2 wird geprüft, ob die aktuelle Zahl ungerade ist. Wenn das der Fall ist, kann es sich nicht um die gesuchte Zahl 4 handeln. Wir verwenden dann die continue Anweisung, um mit der nächsten Iteration zu beginnen, womit die Bedingung in Zeile 5 nicht ausgeführt wird.
Wie überspringt man eine „innere Schleife“?
Nehmen wir wieder das erste Codebeispiel aus PHP und verwenden hier die break Anweisung anstelle einer goto Anweisung:
$array = [
'one' => [ 1, 2, 3 ],
'two' => [ 4, 5, 6 ],
];
foreach ( $array as $key => $numbers ) {
foreach ( $numbers as $number ) {
if ( $number === 4 ) {
break 2;
}
}
}
if ( $number === 4 ) {
printf( "There is a 4 in array '%s'", $key );
} else {
echo "There is no 4 in the array";
}
In PHP können wir break 2 verwenden, um nicht nur die „innere Schleife“, sondern auch die „äußere Schleife“ zu verlassen. Die Zahl nach break gibt also an, aus wie vielen „Kontrollstrukturen“ wir ausbrechen wollen. Etwas Ähnliches ist mit continue 2 möglich, wobei das Programm mit der nächsten Iteration der „äußeren Schleife“ fortfahren würde.
In JavaScript ist dies jedoch nicht möglich. Ihr könnt nach einer break oder continue Anweisung keine Zahl verwenden. Und genau hier kommen die Labeled Statements ins Spiel.
Ausbrechen aus einer inneren Schleife in JavaScript
Wenn wir versuchen, denselben Code wie oben in JavaScript zu erstellen, können wir eine beschriftete Anweisung wie in diesem Code verwenden:
var array = {
one: [1, 2, 3],
two: [4, 5, 6],
};
outerLoop:
for ( var key in array ) {
var numbers = array[key];
for ( var i = 0; i < numbers.length; i++ ) {
if ( numbers[i] === 4 ) {
break outerLoop;
}
}
}
if ( numbers[i] === 4 ) {
console.log( `there is a 4 in array ${key}'` );
} else {
console.log( "There is no 4 in the array" );
}
Das Label muss nicht in einer eigenen Zeile stehen, es kann auch in einer Zeile mit der for Schleife stehen. Zur besseren Lesbarkeit habe ich es einfach so formatiert.
Wenn ihr jeder Schleife ein Label voranstellt, können ihr jeweils eine andere break Anweisung verwenden, um zu einer beliebigen Schleife zu springen. Das Gleiche gilt für continue.
Wofür sollte ich das verwenden?
Sollten ihr also Labeled Statements verwenden? Mein Rat wäre ähnlich wie der, den der xkcd-Comic über goto gibt: Verwendet sie nicht!
Wenn ihr Code schreibt, der von etwas wie break 2, continue 2 oder dem JavaScript-Äquivalent mit einem Labeled Statement profitieren würde, wäre das für mich ein klares Zeichen, dass dieses Stück Code überarbeitet werden sollte.
Anstatt ein goto zu verwenden, um aus einer Schleife herauszuspringen, sollten ihr diese Schleife in eine Funktion verschieben und return verwenden. Und für verschachtelte Schleifen könnt ihr auch verschachtelte Funktionen in Schleifen verwenden.
Im vorherigen Code gibt es viele Dinge zu optimieren, nicht nur die verschachtelten Schleifen. Mit einigen modernen JavaScript-Funktionen könnte er so aussehen:
const array = {
one: [1, 2, 3],
two: [4, 5, 6],
};
function findTheFour(obj) {
for (const property in obj) {
if (obj[property].includes(4)) {
return property;
}
}
return null;
}
const found = findTheFour(array);
if ( found ) {
console.log( `there is a 4 in array ${found}'` );
} else {
console.log( "There is no 4 in the array" );
}
Ist das nicht viel einfacher zu lesen? Wenn wir die Funktion includes() für Arrays verwenden, können wir sogar die „innere Schleife“ vermeiden. Und wir brauchen keine Labeled Statements.
Fazit
Ihr werdet euch jetzt vielleicht fragen, warum ich euch dieses Feature von JavaScript gezeigt habe, und dann von der Verwendung abrate. Nun, es gibt Code, der sie verwendet, den ihr dann vielleicht nicht versteht, wenn ihr noch nie Labeled Statements oder goto in anderen Programmiersprachen gesehen habt. Und ich habe euch noch nicht mal gezeigt, was man damit alles machen kann. Lest Sie also ruhig die Dokumentation dazu, und vielleicht finden ihr ja einen Anwendungsfall, bei dem es für euer Codebasis wirklich von Nutzen sein kann, was immer das auch sein mag.
Im ersten Debugging-Blogbeitrag haben wir die Methode console.log() kennengelernt und erfahren, wie sie zum Debuggen von skalaren Typen oder Arrays verwendet werden kann. Aber es gibt einen schöneren Weg, um Arrays oder Objekte im Browser zu protokollieren.
Die Funktion console.table()
Wie im ersten Blogbeitrag erwähnt, gibt es im console Objekt einige weitere nützliche Funktionen und heute wollen wir uns die table() Funktion ansehen.
Einfache Arrays
Die Funktion kann verwendet werden, um Arrays wir hier zu protokollieren:
Dies ist ein einfaches Beispiel mit einem eindimensionalen (numerisches) Array. Das Ergebnis in der Browser-Konsole sieht dann so aus:
Wie ihr hier sehen könnt, erhalten wir eine Tabelle mit zwei Spalten. Die erste ist der numerische „(index)“ für das Array, und die zweite enthält den „Value“ für jeden Eintrag des Arrays. Die Variable befindet sich immer unterhalb der Tabelle, genau wie bei console.log(), und ihr könnt sie hier „aufklappen“, um sie weiter zu untersuchen.
Mehrdimensionale Arrays
Die Funktion kann nicht nur mit einfachen eindimensionalen Arrays verwendet werden, sondern auch mit mehrdimensionalen:
Jetzt haben wir ein Array von (numerischen) Arrays und so sieht es in der Konsole aus:
(index)
0
1
0
'Leia'
'Organa'
1
'Luke'
'Skywalker'
2
'Han'
'Solo'
Wir haben jetzt drei Spalten. Da die „inneren Arrays“ ebenfalls numerisch sind, haben wir für sie Spaltennamen „0“ und „1“.
Objekte protokollieren
Die Funktion kann nicht nur für Arrays, sondern auch für Objekte verwendet werden. Die Ausgabe hängt von der Struktur der Objekte ab.
Einfache Objekte
Lasst uns ein Objekt für eines der vorherigen Beispiele erstellen:
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
const luke = new Person("Luke", "Skywalker");
console.table(luke);
Und so würde dieses Objekt in der Browser-Konsole protokolliert werden:
(index)
Wert
firstName
'Luke'
lastName
'Skywalker'
Arrays von Objekten protokollieren
Wie wäre es mit der Protokollierung eines Arrays mit mehreren Objekten desselben Typs? Wieder ein Beispiel wie zuvor:
const leia = new Person("Leia", "Organa");
const luke = new Person("Luke", "Skywalker");
const han = new Person("Han", "Solo");
console.table([leia, luke, han]);
Und das ist die resultierende Tabelle für dieses Array von Objekten:
(Index)
firstName
lastName
0
'Leia'
'Organa'
1
'Luke'
'Skywalker'
2
'Han'
'Solo'
Wie viele von euch vielleicht erwartet haben, sind die Spaltennamen jetzt die Eigenschaftsnamen der Objekte.
Protokollierung multidimensionaler Objekte
Im vorherigen Beispiel wurde ein numerisches Array verwendet. Aber wie wäre es mit einem Objekt von Objekten? Hier ist ein Beispiel:
const family = {};
family.mother = new Person("Padmé", "Amidala");
family.father = new Person("Anakin", "Skywalker");
family.daughter = new Person("Leia", "Organa");
family.son = new Person("Luke", "Skywalker");
console.table(family);
Das Ergebnis sieht dann wie folgt aus:
(index)
firstName
lastName
mother
'Padmé'
'Amidala'
father
'Anakin'
'Skywalker'
daughter
'Leia'
'Organa'
son
'Luke'
'Skywalker'
Das sieht doch gut aus, oder? Mit Ausnahme von „(index)“ haben wir einige gute Spaltennamen und unsere Indexwerte sind auch nicht mehr numerisch.
Nur bestimmte Spalten protokollieren
Die Funktion console.table() hat einen zweiten Parameter columns, der ebenfalls ein Array ist. Sie erwartet ein Array mit den Namen der Spalten/Indizes. Für unseren vorherigen Code könnten wir also Folgendes tun:
console.table(family, ["firstName"]);
Und tatsächlich, das wären die protokollierten Ergebnisse:
(Index)
firstName
mother
'Padmé'
father
'Anakin'
daughter
'Leia'
son
'Luke'
Ziemlich toll, nicht wahr? Dies kann bei der Fehlersuche in größeren Objekten mit mehreren Eigenschaften sehr nützlich sein.
Fazit
Auch wenn ihr die allgemeine Funktion console.log() verwenden könnt, um Arrays und Objekte zu debuggen und zu protokollieren, kann es viel einfacher sein, sie in einer Tabelle zu sehen und zu lesen. Außerdem müssen ihr die Tabellen nicht „aufklappen“.
Im ersten Adventskalender-Blogpost zum Thema HTML möchte ich ein HTML-Element vorstellen, das einige von euch vielleicht noch nicht kennen. Es ist nicht wirklich ein eigenes Element, sondern das <input> Element vom Typ color. Mit diesem Element könnt ihr eure Webanwendung einen sehr einfachen Farbwähler hinzufügen.
Hinzufügen eines Farbwählers zu eurer Anwendung
Das Einfügen des Farbwählers ist recht einfach. Es handelt sich um ein <input> Element, das optoinal ein value Attribut haben kann:
Ja, man wollen immer ein <label> zu jedem Formularfeld haben. Wenn ihr das value Attribut nicht benutzt, wird #000000 als Standardwert verwendet. Ihr könnt auch nur einen hexadezimalen RGB-Wert ohne Alphawert (Transparenz) angeben wie also nicht z.B. #00000066.
Der Farbwähler in verschiedenen Browsern
Wie bei vielen anderen Formular-Eingabetypen ist die Benutzeroberfläche des Farbwählers sehr unterschiedlich, je nachdem, welches Betriebssystem und welchen Browser man verwendet. Hier sind einige Beispiele:
Chrome bringt seinen eigenen Farbwähler mit und er sieht unter Linux und Windows ähnlich aus. Firefox hingegen verwendet den systemeigenen Farbwähler des Betriebssystems.
Erweiterte Verwendung mit einer Farbliste
Ein wenig versteckt in der Dokumentation des Elements könnt ihr sehen, dass das Element ein list Attribut verwenden kann. Dieses Attribut allein wäre wahrscheinlich einen eigenen Blogbeitrag wert. Wenn ihr das Attribut verwenden, könnt ihr auf eine <datalist> mit einigen Werten verweisen. Das würde in etwa so aussehen:
Für die <option> Werte könnt ihr erneut nur hexadezimale RGB-Werte verwenden. So sieht der Farbwähler mit der Liste in Chrome aus:
Wenn ihr mehr als 5 Werte habt, werden in bis zu drei Zeilen mit jeweils 5 Farben angezeigt. Bei noch mehr Werten wird eine Scrollbar angezeigt. In Firefox unter Windows wird hingegen derselbe Farbwähler wie zuvor verwendet, und die Optionen werden unter „Benutzerdefinierte Farben“ im Fenster aufgelistet.
Bonus: Verwendung von WordPress-Farbwähler-Komponenten
Wenn ihr ein WordPress-Projekt entwickelt, können ihr die Component für eine Farbwauswahl verwenden, die in der WordPress JavaScript-Bibliothek verfügbar sind.
Die ColorPicker-Komponente
In @wordpress/components finden ihr die ColorPicker Komponente, die einen Farbwähler umsetzt:
import { useState } from 'react';
import { ColorPicker } from '@wordpress/components';
function MyColorPicker() {
const [color, setColor] = useState();
return (
<ColorPicker
color={color}
onChange={setColor}
enableAlpha
defaultValue="#000"
/>
);
}
Wenn ihr die Komponente verwenden, wird sie „inline“ gerendert, das heißt, sie wird nicht erst durch einen Klick auf einen Button geöffnet:
Falls ihr einen Button zum Öffnen des Farbwählers haben möchtet, müsstet ihr das selbst implementieren. Wenn ihr das enableAlpha Attribut hinzufügt, können ihr auch einen RGB-Wert mit Alpha (z. B. #00000066) im Input verwenden. Oder ihr wechselt von „Hex“ zu „RGB“ oder „HSL“, wo ihr dann einen Alphakanal-Schieberegler verwenden könnt.
Die PanelColorSettings
Es gibt derzeit keine Dokumentation für die PanelColorSettings Komponente, aber so könnt ihr sie verwenden:
Im colorSettings Objekt können ihr mehrere Farben einstellen. In diesem Beispiel haben wir eine für die Text- und eine für die Hintergrundfarbe. So würde es angezeigt werden:
Wie ihr hier sehen könnt, zeigt das PanelColorSettings keinen allgemeinen Farbwähler an, wie es der ColorPicker tun würde. Stattdessen werden euch die Farbvorgaben aus dem Theme und aus dem Core angezeigt, je nachdem, wie diese Farbeinstellungen in der theme.json Datei definiert sind.
Fazit
Die Implementierung eines Farbwählers war noch nie so einfach wie mit dem nativen HTML-Eingabetyp. Es ist nicht mehr nötig, eine umfangreiche jQuery/JavaScript-Bibliothek zu verwenden. Es sei denn, man möchte einen Farbwähler innerhalb eines WordPress-Projekts verwenden und auch die Farbpalette des Themes nutzen. Dann kann man die Komponenten aus dem Block-Editor verwenden. Ihr habt dann auch den Vorteil, dass der Farbwähler in jedem Browser und jedem Betriebssystem gleich dargestellt wird.
Wenn ihr einen schnellen Farbwähler für einen beliebigen Anwendungsfall benötigen, können Sie auch einfach nach „Farbwähler“ googeln. 😉
Wir alle kennen diese Websites mit einem „Sticky Header“. Sie haben oben eine horizontale Navigation, oft mit dem Logo der Website auf der linken Seite – das auch oft viel zu groß ist, womit dann nicht mehr viele Menüpunkte daneben passen. Und dann scrollt man auf diesen Seiten mit dieser großen Kopfzeile nach unten … und die Kopfzeile scrollt nicht nach oben weg. Viele Leute wollen das für ihre eigene Website, aber viele Leute, die diese Seite benutzen, mögen es nicht so sehr. Ich bin einer von ihnen.
Probleme mit fixierten Kopfzeilen
Solche fixierte Kopfzeilen bringen gleich mehrere Probleme mit sich. Es gibt zwar ein paar gute Anwendungsfälle, aber meistens werden sie aus den falschen Gründen verwendet – z. B. wollen Unternehmen immer ihre Logos oder wichtige Navigationspunkte sehen. Meiner Meinung dazu: wenn man die Hauptnavigation benutzen muss, um etwas zu finden, macht man etwas beim Inhalt der Website falsch. Aber was sind eigentlich die Probleme?
Problem 1: Elemente liegen hinter oder vor dem Sticky Header
Wenn ihr eine Website mit einem Sticky Header gestaltet, dann habt ihr wahrscheinlich sichergestellt, dass der Inhalt nach unter gerückt wurde. Da der Sticky Header beim Scrollen aber über dem Inhalt liegen soll, kann er auch Inhalte verdecken, mit denen man dann nicht mehr interagieren kann, z. B. ein Popup, das oben rechts den Button zum Schließen hat und nun hinter dem Header liegt. Und da man bei Popups oft nicht durch die Seite blättern kann, während sie geöffnet sind, kann man die Schaltfläche zum Schließen nicht drücken.
Es kann aber auch Probleme mit Inhalten geben, die vor der Kopfzeile erscheinen, wenn man die Seite nach unten scrollt. Das passiert häufig bei eingebetteten Elementen, z. B. einem Video, einer PDF-Datei oder einer eingebetteten Karte.
Problem 2: Ankerlinks funktionieren nicht mehr
Wenn man einen Anker-Link verwendet, um zu Bereichen der Website zu springen – was auf „One-Pager“ Websites sehr beliebt ist. Ein solches Ankerelement scrollt die Website nach unten (oder oben), um das Element dann am oberen Rand der Seite zu positionieren. Und dann befindet es sich hinter Ihrer Kopfzeile.
Problem 3: Zu wenig vertikaler Platz
Wenn die Kopfzeilen sehr hoch sind, bleibt nicht mehr viel vertikaler Platz auf der Seite, um den Inhalt anzuzeigen.
Nehmen wir einen 11″-Laptop mit 720px Höhe. Das ist nicht so ungewöhnlich, wie ihr vielleicht denken. Dann berechnen wir mal die verfügbare vertikale Höhe:
Insgesamt: 720px
Windows-Taskleiste: 66px
Chrome Tableiste: 40px
Chrome Adressleiste: 48px
Chrome Lesezeichenleiste: 34px
Damit bleiben uns nur noch 532px vertikaler Platz. Und nun reduzieren wir diesen um weitere 60px – das ist ungefähr die aktuelle Höhe des Headers in meinem Blog, der nicht wirklich hoch ist. Damit bleiben nur noch 472px verfügbarer Platz. Und haben wir schon über diese Sticky-Cookie-Banner am unteren Rand gesprochen? Das ist dann ungefähr noch so viel Platz, wie ihn viele von uns im Querformat auf unseren Smartphones haben, und wer surft schon gerne im Querformat auf einer Website?
Lösungen
Es gibt mehrere Lösungen für die genannten Probleme. Hier sind einige, die in eurem Fall funktionieren könnten.
Problem 1
Wenn ein einzelnes (typischerweise) eingebettetes Element die Kopfzeile überlagert, müsst ihr einen guten Wert für den z-index für dieses Element finden und ihn überschreiben. Oder ihr erhöht den z-index des Headers. Dies kann jedoch zu einem Problem mit Elementen hinter der Kopfzeile führen. Wahrscheinlich probiert ihr nun viele manuelle z-index Änderungen für mehrere Elemente aus und finden trotzdem einige Elemente, die nicht so erscheinen, wie ihr es wünscht.
Problem 2
Die meisten Websites verwenden CSS-Angaben wie diese, um eine Kopfzeile zu fixieren und den Inhalt nach unten zu verschieben:
Etwas in dieser Art würde für meine Kopfzeile funktionieren. Es ist auch das, was WordPress macht, wenn es die „Adminbar“ anzeigt. Aber es ist nicht die beste Lösung, um einen Sticky Header zu erstellen.
Es gibt einen relativ neuen Wert sticky für die position CSS-Eigenschaft und so würde der CSS-Code für einen Sticky Header mit diesem neuen Wert aussehen:
.fixed-header {
position: sticky;
top: 0;
}
Das Tolle daran: Die Kopfzeile muss nicht das erste/oberste Element auf der Seite sein. Ihr könntet ein Logo haben, gefolgt von der zu fixierenden Kopfzeile und sobald die Kopfzeile beim Herunterscrollen den oberen Rand des Browserfensters erreicht hat, bleibt sie oben „kleben“. In der Vergangenheit war dies nur möglich, indem man die position mit JavaScript auf fixed gewechselt und gleichzeitig margin-top hinzufügt hat. Moderne Browser können diese Art von Effekt jetzt automatisch umsetzen.
Unabhängig davon, wie wir unsere Kopfzeile fixiert, haben wir immer noch das Problem, dass Ankerlinks nicht funktionieren. Das margin von 60px wird verschiebt nicht alle Elemente um diesen Wert nach unten, sondern nur den Anfang des Seiteninhalts.
Um dieses Problem zu lösen, könnt ihr eine zusätzliche CSS-Eigenschaft verwenden, um dem Anker einen Versatz für das Scrollen zu geben, in etwa so:
html {
scroll-padding-top: 60px;
}
Dieses zusätzliche scroll-padding-top sollte einen Wert erhalten, der mindestens der Höhe der Kopfzeile entspricht. Und hier sehen wir ein weiteres Problem mit dieser Lösung: Wenn sich die Höhe der Kopfzeile ändert, z. B. wenn die Elemente nicht mehr in eine Zeile passen und in zwei Zeilen aufgeteilt werden, dann funktionieren diese statischen Werte nicht mehr. Man könnte jetzt ein wenig JavaScript hinzufügen, um diese beiden Werte dynamisch zu aktualisieren, aber das ist keine gute Lösung.
Problem 3
Meine Lieblingslösung für dieses Problem: Verwenden Sie einfach keinen Sticky Header. Damit sind auch alle anderen Probleme gelöst. Nein, im Ernst. Muss eure Kopfzeile wirklich fixiert sein? Macht es eure Website einfacher zu benutzen? Kann man die Inhalte besser lesen oder sich sogar nur dadurch zurechtfinden? Überlegen es euch zweimal, bevor ihr eine Kopfzeile sticky macht und macht es nicht nur, weil „es euch so gefällt“.
Für den Fall, dass die Kopfzeile fixiert werden soll, gibt es einige Möglichkeiten, um die Nutzbarkeit der Website zu verbessern:
Verringert die Höhe des Headers, wenn die Website nach unten scrollt – das machen viele Themes
Versteckt die Kopfzeile nach dem Scrollen nach unten und zeigt sie wieder an, wenn man nach oben scrollt – viele Apps machen das
Macht den Header „nicht sticky“, wenn ihr eine niedrige Anzeigehöhe ermittelt oder ihr erkennt, dass ein Gerät im Hochformatmodus verwendet wird
Fazit
Manche Leute lieben fixierte Kopfzeilen, andere hassen sie. Ihr müsst für euch selbst entscheiden, ob ihr sie haben wollt. Aber bitte tun eines: testet eure Website nicht nur auf einem großen Desktop-/Laptop-Bildschirm. Nehmen auch mal euer Smartphone zur Hand und schaut euch die Website im Querformat an. Glauben ihr immer noch, dass ein Sticky Header eine gute Idee ist? Dann wendet bitte die oben genannten Techniken an, um die üblichen Probleme zu lösen.
Nein, ich werde keinen Videokanal zu diesem Thema starten. Aber ich möchte euch einige Dinge über Zahlen zeigen, die ihr vielleicht noch nicht kanntet. Die Antwort ist natürlich, wie wir alle wissen, 42.
Arten von Zahlen
Fangen wir mit den ganz grundlegenden Dingen an. Ihr wisst wahrscheinlich alle, dass es in JavaScript, wie in vielen anderen Programmiersprachen auch, Dezimalzahlen und Fließkommazahlen gibt. Aber intern sind alle vom Typ Number. Sie sind 64-Bit lang, was für den meisten Code ausreichen sollte. Wenn ihr aber wirklich mal große Zahlen benötigen, können ihr diese auch in einem BigInt speichern.
Einige weniger bekannte Zahlentypen
Nachdem wir nun wissen, dass alle (kleinen) Zahlen vom Typ Number sind, kennen wir nur einen Teil der Wahrheit. Wir können mehr als nur zwei Typen haben. Hier sind die zwei typischen Typen und einige weitere, von denen ihr vielleicht noch nie gehört oder die ihr in JavaScript noch nie verwendet habt:
42 // dezimal
42.0 // float
042 // oktal => 34 dezimal - da die Ziffern nach Null(en) am Anfang <= 8
099 // nicht oktal - dezimal 99, da die Ziffern nach Null(en) am Anfang > 8
0o42 // explizite oktale Zahl - mit kl. oder gr. lateinischen "o"
0x42 // hexadezimal => 66 dezimal - mit kl. oder gr. lateinischen "x"
0b101010 // binär 42, schöne Folge ;) - mit kl. oder gr. lateinischen "b"
0.042e3 // Exponentialzahl 42 - mit kl. oder gr. lateinischen "e"
42n // BigInt 42 - dezimal gefolgt von kl. lat. "n" auch für kleine Zahl
Vergleich von Zahlen
Wie wir bereits besprochen haben, sind, mit Ausnahme von BigInt, alle Zahlen vom gleichen Typ. Wenn man also die folgenden Zahlen vergleicht, sind das die Ergebnisse:
Das letzte könnte (ein bisschen) unerwartet sein. Wenn Sie also eine BigInt mit einer anderen Zahl vergleichen möchtet, könnt ihr den „strengen Vergleichsoperator“ nicht verwenden, da die Zahlen von unterschiedlichem Typ sind.
Wenn ihr herausfinden wollt, ob eine Zahl eine ganze Zahl ist, können ihr eine Funktion aus dem Number Objekt verwenden:
Wie ihr seht, gibt diese Funktion true zurück, wenn es sich bei der Zahl um eine Ganzzahl oder eine Fließkommazahl mit einem Nachkommawert von 0 handelt – oder um einen wirklich kleinen Bruch. Leider gibt es keine ähnlichen Funktionen, um auf andere Zahlentypen zu prüfen.
Jetzt kommt der lustige Teil
Benötigen ihr sehr lange Zahlen in eurem Code? Dann werden ihr wahrscheinlich etwas erhalten, das wirklich schwer zu lesen ist. Aber es gibt einen netten kleinen Trick, um sie besser lesbar zu machen:
// Das ist eine riesige Zahl!
let rubiksCubeConfigurations = 43252003274489856000;
// Als String ist es einfacher zu lesen, aber das ist schlechter Code!
let sameNumberAsString = parseInt("43252003274489856000");
// Wie wäre es damit?
let easierToRead = 43_252_003_274_489_856_000;
Das ist doch schon besser lesbar, oder etwa nicht? Das Lustige daran ist, dass man einen Unterstrich an jeder beliebigen Stelle verwenden kann, nur nicht am Anfang oder Ende der Zahl, nicht nach einer führenden Null und nicht zwei Unterstriche hintereinander. Aber viele von uns werden sie wahrscheinlich wie oben gezeigt verwenden.
Bonus: Zahlen formatieren
Nachdem wir nun gesehen haben, wie man Zahlen im Code besser lesbar macht, hier noch ein Tipp, um sie für Menschen aus anderen Ländern leichter lesbar zu machen. In den USA würden wir eine Zahl wie folgt schreiben: „123,456.789“. Aber in Deutschland würde man „123.456,789“ schreiben. Wenn wir uns das Number Objekt noch einmal ansehen, finden wir die Funktion toLocaleString(), die wir wie folgt verwenden können:
Hättet ihr gewusst, wie die Zahlen in den verschiedenen Sprachen formatiert werden? Und in manchen Sprachen gibt JavaScript sie sogar mit nicht-lateinischen Buchstaben aus. Die gleiche Funktion kann auch für die Formatierung von Währungen verwendet werden:
const costs = 123.4567;
// German with EUR
console.log(
costs.toLocaleString("de-DE", { style: "currency", currency: "EUR" }),
); // 123,46 €
Fazit
Es gibt viele wirklich nützliche Tricks, um mit Zahlen in JavaScript umzugehen. Ich hoffe, es war auch etwas für auch dabei, das euch bei einem zukünftigen Projekt helfen wird. Ich kann auch empfehlen, sich die Dokumentation der anderen Funktionen und Eigenschaften des Number Objekts anzusehen.
Es ist mal wieder so weit: Das Jahr neigt sich dem Ende zu. Einige von Ihnen haben gerade Thanksgiving gefeiert, während der Rest der Welt nur den Black Friday „gefeiert“ hat. Aber ich spreche von den 24 Tagen vor Weihnachten. Ich hatte mir 2015 mir das Ziel gesetzt, jeden Tag einen Blogbeitrag zu schreiben. Das habe ich 2015 und 2016 auf Deutsch auch geschafft. Ich habe dann 2017 versucht, auch Englisch zu schreiben, aber nach nur 10 Tagen musste ich aufhören. Seitdem habe ich es nicht mehr versucht.
Neustart des Adventskalenders
Dieses Jahr werde ich es wieder versuchen. Bei den vergangenen Ausgaben hatte ich nicht wirklich alle Themen geplant, wenn überhaupt irgendwelche. Aber in diesem Jahr war ich eine Woche früher bereit und habe bereits 24 potenzielle Themen gesammelt, über die ich schreiben kann. Ich werde mich auf Frontend-Themen und Debugging konzentrieren. Jeden Tag werde ich über eines dieser vier Themen schreiben:
Debugging
JavaScript
CSS
HTML
Die Themen werden sich immer abwechseln. Heute bekommt ihr also ein Debugging-Thema und dann wieder in 4 Tagen. Das Thema Debugging kam mir in den Sinn, weil wir im Team eine interne Lern-Sitzung zu Xdebug hatten und uns im Anschluss daran mit dem Debugging im Browser beschäftigt haben. In meinen 6 Blogbeiträgen zu diesem Thema werde ich mich auf das Debugging im Browser konzentrieren.
Logging im Browser
Das Thema Debugging ist sehr umfangreich und moderne Browser bieten zahlreiche Möglichkeiten, Dinge zu debuggen. Fangen wir also mit etwas Grundlegendem an: dem Protokollieren von Dingen in JavaScript.
Wenn ihr einen Code in JavaScript schreibt, möchten ihr ihn vielleicht debuggen und den Wert bestimmter Dinge im Ablauf eines Stücks eures Codes sehen. Dazu können Sie die Funktion console.log() verwenden:
console.log(some_variable);
Ihr könnt diese Zeile in eine JavaScript-Datei in einem (externen) JavaScript-Code einfügen, den ihr in eurem Projekt verwenden, aber ihr könnt sie auch direkt in der Browser-Konsole verwenden. Alle modernen Browser verfügen über eine solche JavaScript-Konsole. In Chrome kann man sie mit „STRG + UMSCHALT + J“ (Windows und Linux) öffnen.
Einige Werte protokollieren
Nach dem Öffnen der Registerkarte „Console“ in den DevTools können ihr beliebigen JavaScript-Code in die „Eingabezeile“ am Ende der Konsole eingeben. Lasst uns hier mal etwas debuggen:
In diesem sehr einfachen Beispiel testen wir den Wert der aktuellen URL der Website. Die erste Zeile nach unserem ausgeführten Code ist die Ausgabe der Funktion console.log(). Die zweite Zeile mit dem undefined Wert ist die Rückgabe der Funktion selbst.
Wenn ihr die Browser-Konsole verwendet, brauchen ihr die Funktion console.log() nicht einmal. Ihr könnt den JavaScript-Code direkt in die Zeile eingeben und die Eingabetaste drücken. Die console.log() Funktion wird normalerweise in einem (externen) Skript auf eurer Website verwendet:
Nehmen wir diesen Beispielcode. Wir haben ein Array mit Farben und wollen die Werte großschreiben. Wir verwenden die Funktion map() für das Array. Innerhalb der Funktion loggen wir das aktuelle item und nach der Ausführung der Funktion die resultierende Variable capitalizedColors. Die Protokollierung im Browser sieht wie folgt aus:
Da das Array drei Elemente hat, sehen wir drei Zeilen mit dem jeweiligen Wert. Am Ende jedes protokollierten Wertes sehen wir einen Verweis index.html:6, der den Dateinamen und die Zeile in dieser Datei angibt, in der das Logging durchgeführt wurde. Wir sehen auch das protokollierte Array, das ich bereits durch Klicken auf den kleinen Pfeil erweitert habe. Chrome zeigt uns sogar die Länge des Arrays und den „[[Prototype]]“ für das Array an. Wenn wir diesen aufklappen, können wir alle Eigenschaften und Funktionen des Arrays sehen. Hier würden wir auch die map() Funktion finden, die wir gerade verwendet haben.
Fazit
Das sollte für heute genug sein. Viele von Ihnen werden diese Funktion wahrscheinlich schon kennen. Aber wusstet ihr, dass es noch mehr Debugging-Funktionen gibt? In den folgenden Blogbeiträgen im Dezember werden wir einige davon genauer unter die Lupe nehmen.
Ich verwende hauptsächlich die Chrome DevTools, aber ihr könnte auch die Firefox DevTools auf ähnliche Weise verwenden. Wenn ihr Safari, Edge, Brave oder einen anderen gängigen Browser verwendet, sucht einfach nach Dokumentation dazu, wie ihr mit diesen Tools arbeiten könnt. Probieren es also aus und debugge deinen eigenen JavaScript-Code.
Da ich mein aktuelles Projekt mit dem neuen Thema erst einmal gestoppt habe, kann ich auch wieder über andere Themen zu schreiben. Diese Woche möchte ich über ein wichtiges globales HTML-Attribut schreiben: das lang-Attribut.
Vielleicht haben einige von euch dieses Attribut noch nie (aktiv) verwendet, aber es ist ziemlich wichtig. Es teilt nicht nur Suchmaschinen mit, in welcher Sprache der Inhalt eurer Website verfasst ist, sondern auch einer „Assistive Technology“ wie einem Screenreader, welche Stimme es beim Lesen des Textes verwenden soll. Es kann sogar „inline“ für ein einzelnes Wort oder Teile eines Textes gesetzt werden.
Das wichtigste lang Attribut wird aber im <html> Tag gesetzt. In WordPress wird das durch die Funktion language_attributes() erledigt, die man normalerweise in der Datei header.php eines klassischen Themes finden. In einem Block-Theme wird dies automatisch im Core erledigt.
Gründe für das Überschreiben des lang-Attributs
Normalerweise möchten man den Wert des lang Attributs nicht ändern, da WordPress immer den richtigen Wert verwendet, je nach Spracheinstellung eurer Website. Es gibt aber auch Fälle, in denen man es ändern möchte.
Mehrsprachige Websites
WordPress kann nur eine Frontend-Sprache haben, es sei denn, man installieren ein Plugin für Mehrsprachigkeit. Ich verwende MultilingualPress, welches auf Multisite basiert. In einer WordPress-Multisite kann man die Sprache für jede Unterseite festlegen. Dadurch wird automatisch das richtige lang Attribut in jeder Site verwendet.
Wenn deine Website kein Mehrsprachigkeit-Plugin verwendet, du aber eine Seite mit einer anderen Sprache hast, kannst du das lang Attribut mit etwas Code überschreiben.
Laden von externem Code
Ein weiterer Anwendungsfall ist die Verwendung von Plugins, die das lang Attribut zum Laden externer Daten verwenden. Ich bin diese Woche auf ein Cookie-Banner-Plugin gestoßen, das den Text für das Banner von einer externen Ressource lädt. Es hat den genauen Wert des lang Attributs verwendet. Dabei hat aber es einen Wert wie en erwartet, also einen Wert mit nur zwei Zeichen. WordPress verwendet jedoch einen Wert wie en-US, was für dieses Cookie-Banner nicht funktionieren würde. Wir müssen also den zweiten Teil des Wertes entfernen.
CSS verwendet das Attribut
Ein gutes Beispiel für einen Anwendungsfall in CSS ist die quotes Eigenschaft. In verschiedenen Sprachen werden unterschiedliche Anführungszeichen verwendet. Wenn man die richtigen Anführungszeichen in einem <q> HTML Tag verwenden möchte, muss man normalerweise nichts tun. Der Browser übernimmt dies für einen, weil der Wert auf quotes: auto gesetzt ist. Wenn man das überschreiben will, kann man Folgendes tun:
q {
quotes: "«" "»" "‹" "›";
}
Dies würde immer die Anführungszeichen verwenden, die im Französischen und anderen Sprachen verwendet werden, auch wenn das lang Attribut auf en gesetzt ist.
Einige CSS-Bibliotheken verwenden das lang Attribut wie folgt, um Stile zu ändern:
[lang="en"] q {
/* Some styles */
}
Dies würde nicht funktionieren, wenn der Wert für das lang Attribut auf en-US gesetzt ist. Es gibt allerdings die CSS-Pseudoklasse :lang(), mit der es funktionieren würde:
:lang(en) {
/* Some styles */
}
Wenn man hier en verwenden, würde es auch für die Werte en-US, en-GB, usw. funktionieren. Aber wenn man es auf en-US setzt, würde es umgekehrt nicht auch nur für den Wert en funktionieren.
Da das CSS in einem solchen Framework statisch sein könnte, wäre das Überschreiben vielleicht etwas zu kompliziert. In diesen Fällen möchte man einfacher den Wert des globalen lang Attributs des <html> Tags ändern.
Wie ändert man den Wert?
Angenommen, wir wollen den Wert für eine bestimmte Seite auf einen anderen statischen Wert ändern, dann könnte man es wie folgt umsetzen:
Dies würde das lang Attribut des <html> Tags für jede Seite und jeden Beitrag mit „english“ im Permalink mit lang="en-US" überschreiben.
Wie man aus der Funktion herauslesen kann, würde der Filter nicht nur den Wert, sondern auch den Namen des Attributs zurückgeben. Wenn man sich den vollständigen Code der Funktion get_language_attributes ansieht, wird klar, dass die Funktion auch andere Attribute wie dir zurückgeben kann:
function get_language_attributes( $doctype = 'html' ) {
$attributes = array();
if ( function_exists( 'is_rtl' ) && is_rtl() ) {
$attributes[] = 'dir="rtl"';
}
$lang = get_bloginfo( 'language' );
if ( $lang ) {
if ( 'text/html' === get_option( 'html_type' ) || 'html' === $doctype ) {
$attributes[] = 'lang="' . esc_attr( $lang ) . '"';
}
if ( 'text/html' !== get_option( 'html_type' ) || 'xhtml' === $doctype ) {
$attributes[] = 'xml:lang="' . esc_attr( $lang ) . '"';
}
}
$output = implode( ' ', $attributes );
/**
* Filters the language attributes for display in the 'html' tag.
*
* @since 2.5.0
* @since 4.3.0 Added the `$doctype` parameter.
*
* @param string $output A space-separated list of language attributes.
* @param string $doctype The type of HTML document (xhtml|html).
*/
return apply_filters( 'language_attributes', $output, $doctype );
}
Plugins könnten sich ebenfalls in diesen Filter einhängen, womit das Überschreiben von $output mit etwas Statischem möglicherweise nicht funktioniert. Unglücklicherweise gibt es auch keinen Filter, der nur den $lang Wert ändert, und ein Hook in get_bloginfo(), um die Sprache zu überschreiben, könnte an einigen anderen Stellen zu Problemen führen, an denen dieser Code verwendet wird. Wenn man den zweiten Teil des Wertes entfernen will, könnten man aber einen regulären Ausdruck wie diesen verwenden:
Wenn man etwas noch Komplexeres benötigen, ist es wahrscheinlich am besten, einfach die gesamte Funktion zu überschreiben.
Fazit
Das lang Attribut ist ein sehr wichtiges Attribut, das jede Website immer setzen sollte. Aber der Wert ist vielleicht nicht immer so, wie man ihn brauchen. Für diese Fälle gibt es einen Filter, mit dem der Wert überschrieben werden kann. Dabei sollte man aber immer darauf achten, dass man keinen ungültigen Wert zurückgibt.
Mein letzter Blogbeitrag war im Juli. Ich habe es nicht geschafft, meinen üblichen Rhythmus von einem neuen Blogbeitrag alle zwei Wochen einzuhalten. Dafür gab es viele verschiedene Gründe, einige davon waren persönliche und einige betrafen Menschen, die mir wichtig sind. Aber ein anderer Grund war der sehr langsame Fortschritt an meinem neuen Theme. Und heute muss ich sagen: Stopp!
Ich habe die Demo-Inhalte verwendet, die ich normalerweise verwende, wenn ich ein Theme programmiere. Dabei habe ich versucht, jedes kleine Element „so pixelgenau wie möglich“ zu replizieren. Aber da ich jetzt bei ~900 Zeilen zusätzlichem CSS-Codes angelangt bin und dies die Nutzenden daran hindern würde, Stile im Website-Editor zu überschreiben, funktioniert dieser Ansatz einfach nicht.
Wie soll es mit dem Projekt weitergehen?
Mein Hauptziel bestand darin, ein Theme zu erstellen, das von möglichst vielen Nutzenden des ursprünglichen Waipoua-Themes verwendet werden kann, mit möglichst wenigen „Migrationsschritten“. Aber ich muss feststellen, dass dies einfach nicht machbar ist. Ich wusste immer, dass ich auch ein „Begleit-Plugin“ anbieten müsste, das alle Shortcodes beinhaltet, die Waipoua angeboten hat. Aber ist es das wirklich wert? Wie viele gibt es noch, die Waipoua verwenden und die mutig genug wären zu migrieren?
Option 1: Zwei verschiedene Varianten meines Themes anbieten
Mein erster Gedanke war es schon vor einiger Zeit, zwei Varianten meines Themas anzubieten. Die erste würde es versuchen, nahezu „pixelperfekt“ zu sein, ohne die Stile von Elementen auf bestehenden Websites zu ändern. Die zweite würde den Site-Editor in vollem Umfang nutzen, indem nur Stile über den Site-Editor mithilfe der theme.json hinzugefügt würden.
Bei diesem Ansatz würde die erste Variante diese ~900 Zeilen CSS-Datei laden, die zweite würde nur eine kleinere Datei laden, die für die Gestaltung der Kopfzeile mit der Suche erforderlich wäre. Oder sie würde gar nicht erst versuchen, die Suche genau so zu replizieren, wie sie ist. Stattdessen würde sie einfach eine andere Suche im Header anbieten.
Option 2: Dies als Möglichkeit zum Lernen sehen und weiterziehen
Dieses Projekt hat wirklich Spaß gemacht! Aber es war manchmal auch echt sehr frustrierend. Eines habe ich aber daraus gelernt: Ein gutes Theme zu schreiben ist nicht einfach! Mein Respekt für die Arbeit von Ellen und Manuel ist mit jeder Stunde gewachsen, in der ich versucht habe, ihre Arbeit zu replizieren.
Und weiter geht’s?
Ich habe immer noch vor, ein neues Thema zu erstellen, das Waipoua ähnlich genug ist, damit die Menschen meine Seite wiedererkennen und vielleicht nicht einmal merken, dass es ein anderes Thema ist. Wenn man die neue und die alte Version nicht direkt nebeneinander vergleicht und nur die Farben, die Schriftarten und die Struktur betrachtet, wird man wahrscheinlich denken, dass es nur ein paar kleine Anpassung am aktuellen Theme gab.
Mir gefällt der Gedanke einfach nicht, in meinem allerersten Theme so viel zusätzliches CSS hinzuzufügen zu müssen, welches dann nicht im Site-Editor überschrieben werden kann. Und ich möchte auch nicht erzwingen, dass ein zusätzliches Plugin verwendet werden muss, damit es mit den aktuellen Inhalten funktioniert. Ich selbst gehöre ja auch zu denen, die einige Shortcodes in alten Blogbeiträgen verwendet haben, als es noch keinen Block-Editor gab, mit dem man „Buttons“ oder Spalten im Text erstellen konnte.
Helft mir mit eurem Feedback!
Ich bitte in meinen Blogbeiträgen häufiger um Kommentare, aber meistens bekomme ich keine. Dieses Mal würde ich mich wirklich über Ihr Feedback freuen. Vor allem von allen, die Waipoua auf einer Seite verwenden. Was soll ich eurer Meinung nach tun? Ein Theme mit zwei Varianten erstellen (Option 1)? Oder einfach versuchen, ein echtes Block-Theme zu erstellen, das nicht versucht, „Legacy-Inhalte zu unterstützen“ (Option 2)?
Als ich dieses Projekt angefangen habe, war es mein Ziel, das Theme so weit wie möglich nachzubauen und dabei nur den Website-Editor zu verwenden. Mein Endgegner war der Header. Nur mit den Optionen des Website-Editors und der Core-Blöcke war es mir einfach nicht möglich, das generelle Aussehen zu erreichen. Besonders der Suche-Block war einfach zu eingeschränkt in den verfügbaren Optionen.
Hinzufügen einer CSS-Datei
Schon alleine die Hintergrundfarbe (transparent) zu setzen und das Suche-Icon zu gestalten war nicht möglich. Als ich dann noch versucht habe Styles und Effekte für das Suchfeld zu setzen und den Platzhalter-Text zu stylen, kam ich an einem Punkt, an dem es unmöglich wurde, nur die theme.json Datei zu verenden und ich musste mein Ziel aufgeben und nun doch eine CSS-Datei zum Theme hinzufügen.
Ein paar Änderungen später wurde mir dann bewusst, dass ich sehr viele lange und sich wiederholende CSS-Selektoren schreiben musste und habe mich dann dazu entschieden, auch noch SASS zu verwenden.
Nach dieser Niederlage wurde es dann aber sehr viel einfacher. Da ich mich gut damit auskenne CSS zu schreiben, war das Styling des Headers viel einfacher. In den letzten vier Wochen habe ich am Header und dem Rest des Themes gearbeitet und der aktuelle Stand des Themes kommt schon sehr nahe an das originale Theme heran.
Der Header
Wie zuvor erwähnt war der Header der Grund dafür eine CSS-Datei einzufügen. Aber einige Styles waren sogar nur unter Verwendung des Website-Editors möglich. In einem ersten Versuch konnte ich sogar den Home-Link umsetzen, indem ich die "css" Eigenschaft in den "core/home-link" Block-Styles verwendet habe. Um aber alle Styles des originalen Themes nachzubauen, brauchte ich ~100 Zeilen CSS. Aber das Ergebnis sieht sehr gut aus:
Der originale Header
Der neue Header
Könnt ihr die Unterschiede erkennen? Vermutlich würdet ihr ein Tool brauchen, um diese zu visualisieren. Einige Elemente sind um ein paar Pixel verschoben, aber insgesamt sieht es sehr ähnlichen aus. Für das Home-Icon habe ich eine SVG erstellt, bei der die originale (sehr kleine) PNG als Vorlage diente.
Einen Unterschied, den ihr vielleicht entdeckt habt, ist die Verwendung der „nach unten Caret“ nach den Navigationspunkten mit einer Subnavigation. Hierzu gibt es unter „Darstellung > Untermenüs“ im „Navigation“ Block eine Option, um diese zu deaktivieren, aber da ich mein Theme von Anfang an barrierefreier machen möchte, habe ich diese Option aktiviert gelassen.
Erstellung zusätzlicher Templates
Nachdem ich das initiale Design fertig hatte, habe ich mit einigen Templates weitergemacht. Die ersten beiden waren „Einzelne Beiträge“ und „Seiten“.
Das „Einzelne Beiträge“ Template
Ich dachte, dass das schnell gehen sollte. Nur das Datum und die Anzahl der Kommentare (mit einem Anker-Link zum Kommentar-Formular) im „Eintrags-Header“ einfügen, sowie die Kategorien, Schlagwörter und Kommentare zum „Eintrags-Footer“ hinzufügen. Ich lag so falsch. 🙈
Bei der Entwicklung eines Themes verwende ich in der Regel die „Theme Unit Test“ XML-Import-Datei. Diese erstellt viele Inhalte und Menüs, um euer Design zu testen. Um die verschiedenen HTML Tags und deren Styles im neuen Theme zu testen, habe ich den Beitrag „Template: Comments“ verwendet. Das war dann der Punkt, an dem viel CSS im Theme gelandet ist. Elmastudio hat sich sehr viel Mühe gegeben, die verschiedenen Elemente, die man im Inhalt haben kann, schön zu gestalten, wie etwa Listen, Zitate, Tabelle, usw. Und sie haben diese auch (anders) für Kommentare gestaltet, um der geringen verfügbaren Breite im Kommentar-Inhalt Rechnung zu tragen. Da man aber nur recht wenige dieser nativen Elemente in der theme.json Datei gestalten kann, musste ich mehr CSS schreiben. Am Ende sind ~160 Zeilen für diese Elemente im Inhalt und nochmal ~100 Zeilen für Anpassungen im Kommentar-Inhalt zusammengekommen. Ich realisiere jetzt, wie viel Arbeit es macht und viel Gedanken man sich machen muss, um ein Theme mit tollen Styles zu erstellen und ich habe nun noch mehr Respekt davor, was Ellen und Manuel in all ihren Themes gemacht haben.
Einführung einer neuen Template-Vorlage: comments
Da das die Kommentare-Liste und das Kommentare-Formular sowohl in Beiträgen, als auch in Seiten verwendet wird, habe ich daraus eine neue Vorlage gemacht. Dieser „template part“ enthält die Blöcke „Kommentar-Template“, „Seitennummerierung der Kommentare“ und „Formular für Kommentare“.
Für das Kommentare-Formular musste ich nochmal ~75 Zeilen CSS schreiben (selbst mit SASS) und mir wurde einmal mehr bewusst, wie frustrierend es ist, Styles für Formulare zu schreiben:
Aber hey, am Ende waren es dann doch „nur“ ~75 Zeilen und ich konnte dabei auch noch ein wenig CSS Grid üben.
Beitrags-Navigations-Link
Zwei andere Blöcke, bei denen ich mit der aktuellen Implementierung nicht ganz glücklich bin, sind „Vorheriger Beitrag“ und „Nächster Beitrag“. Sie bieten zwar einen Stil-Variante „Pfeil“ an, aber hier werden die Pfeile vor/nach dem Link eingefügt und können nicht angeklickt werden. Da ich aber ein „Button-Design“ haben wollte, musste ich den „Pseudo-Content-Trick“ verwenden, um den klickbaren Bereich auf den gesamten „Button“ zu vergrößern. Auch das Hinzufügen einer Hintergrundfarbe für den Button war nicht so einfach, da das Wrapper DIV auch dann sichtbar war, wenn sich darin kein Link befand. Insgesamt brauchen diese Blöcke also noch ein paar Verbesserungen in zukünftigen Versionen des Block-Editors.
Das „Seiten“ Template
Nachdem ich das Template für Beiträge fertig hatte, war die Erstellung des Templates für Seiten ziemlich einfach. Ich musste lediglich das Datum und den Kommentar-Link aus dem „Eintrag-Header“ entfernen, sowie die Kategorien und Schlagwörter aus dem „Einträge-Footer“. Der Rest des Templates blieb gleich. Und da wir ja ein „comments“ Template-Vorlage erstellt haben, wird dieser Teil auch viel einfacher, wenn wir mal etwas an der Gestaltung dieses Bereichs ändern wollen.
Der Footer
Nach der Fertigstellung des „Eintrag-Inhalts“ für Beiträge und Seiten, habe ich auch Footer fertiggestellt. Da es fast ausschließlich statischer Inhalt ist, war das schnell getan. Nur beim Speichern der Änderungen ins Theme über das Create Block Theme Plugin gab es einige Probleme.
Statischer Text wird in Übersetzungsfunktionen eingebettet, was toll ist! Leider wurden diese Übersetzungen nicht escaped. Und auch die Jahreszahl war dann fest kodiert. Selbst wenn man das manuell ändern, die Übersetzungen escaped, und die Jahreszahl dynamisch macht, wird er bei jeder Änderung am Footer wieder überschrieben.
Dynamischer Footer mit sicherer Ausgabe
Hier ist ein Teil des Footers, wie er in meiner verbesserten und dynamischen Version aussieht:
Wie ihr sehen könnt, fehlt das Escaping komplett. Weiterhin sind die letzten beiden Absätze nicht mehr übersetzbar. Ich hatte leider nicht die Zeit, mir mal den Code genauer anzusehen, der diese Zeilen erzeugt und zu prüfen, ob es dafür eine Lösung gibt. In der Zwischenzeit muss ich also jedes Mal diese Änderungen rückgängig machen, wenn ich etwas am Footer-Template anpasse.
Fazit
Ein Theme zu erstellen ist verdammt viel Arbeit! Auch wenn man „nur“ ein klassisches Theme als Block-Theme nachprogrammiert, braucht das sehr viel Zeit, denn man muss auf so viele Dinge achten. Und ich habe das Theme noch nicht einmal ausgiebig mobil getestet – der erste Eindruck ist aber gut.
Es gibt noch immer viel zu tun, aber ich hoffe, dass ich den Wechsel in zwei Wochen einhalten kann. Selbst wenn das Theme dann noch nicht perfekt ist.
Falls ihr die anderen Änderungen sehen wollt, auf die ich in diesem Update nicht eingehen konnte, oder wenn ihr weiterhin die Implementierung nachverfolgen wollt, dann seht euch einfach die Commits im GitHub-Repository an. Ich bin sehr zufrieden mit dem aktuellen Stand der Implementierung und kann es nicht erwarten, das Theme endlich fertigzustellen. 🙌