Verschachteltes CSS ohne Präprozessoren

Ich weiß nicht, wann ich angefangen habe, Sass zu verwenden. Mein erster Blogbeitrag zu diesem Thema stammt aus dem Jahr 2012. Es gibt viele Funktionen, für die ich an Sass liebe und eine von ihnen ist die Verschachtelung von Regeln.

Was ist Verschachtelung in Sass?

Ein typisches Beispiel dafür, wann Verschachtelung wirklich nützlich sind, ist der Umgang mit CSS-Selektoren von Elementen, die in der Regel Kinder von anderen Elementen sind. Ungeordnete Listen, die für Navigationsmenüs verwendet werden, sind hier ein gutes Beispiel:

nav {
	ul {
		list-style: none;

		ul {
			display: block;
		}
	}

	li {
		display: inline-block;
	}

	a {
		display: block;
		padding: 5px 10px;
	}
}

In diesem Code haben wir einige Kind- (oder Enkel-) Knoten eines <nav> Tags. Wenn wir nun einen Präprozessor wie Sass verwenden, erhalten wir für diese Verschachtelung das folgende Ergebnis im kompilierten CSS:

nav ul {
  list-style: none;
}
nav ul ul {
  display: block;
}
nav li {
  display: inline-block;
}
nav a {
  display: block;
  padding: 5px 10px;
}

Viele von euch werden wahrscheinlich zustimmen, dass der Sass-Code einfacher zu schreiben und zu ändern ist, da wir nicht den gesamten CSS-Selektor schreiben müssen. Das Umstellen der Verschachtelung ist wirklich einfach, verglichen mit dem Ersetzen der mittleren Teile von langen CSS-Selektoren. Die Selektoren, die ich oben ausgewählt habe, sind recht kurz. Das ist Absicht, denn ihr solltet nicht zu viel verschachteln, nur weil ihr es könnt, oder noch schlimmer, weil es euer HTML abbildet. Wahrscheinlich brauchen wir keinen CSS-Selektor wie „nav ul li a„, denn wenn wir nur jeden Link in der Navigation stylen wollen, reicht es aus etwas wie „nav a“ zu haben.

Wie kann man Verschachtelungen ohne Präprozessor verwenden?

Wenn wir versuchen, diesen schön verschachtelten Code in einer .css-Datei zu verwenden, könnte es einfach funktionieren. Ganz ohne einen Präprozessor zu verwenden. Viele aktuelle Browser unterstützen die oben gezeigt Syntax.

Wenn ihr aber noch einen Browser unterstützen müsst, der diese neue Spezifikation nicht implementiert hat und diese Syntax nicht versteht, dann müsst ihr den Code etwas abändern. Bei diesen Browsern müsst ihr nur ein & vor jedem verschachtelten Selektor hinzufügen. Der obige Code sieht dann folgendermaßen aus:

nav {
	& ul {
		list-style: none;

		& ul {
			display: block;
		}
	}

	& li {
		display: inline-block;
	}

	& a {
		display: block;
		padding: 5px 10px;
	}
}

Glücklicherweise haben alle großen Browser diese neue Spezifikation implementiert, bei der ihr das & nicht vor den verschachtelten Selektoren hinzufügen müssen. Sie unterstützen aber auch die alte Syntax. Wenn ihr also auf Nummer sicher gehen wollt, können ihr noch für einige Zeit die alte Syntax verwenden. Ich kann auch sehr empfehlen, in der Dokumentation ein wenig mehr über CSS-Verschachtelungen nachzulesen.

Fazit

Das war’s! Verwenden einfach Verschachtelungen wie in Sass, Less und anderen Präprozessoren und moderne Browser werden euren Code problemlos verstehen. Wenn ihr noch zusätzlich einen Präprozessor für andere Dinge wie Variablen verwenden, wird dieser diese Syntax trotzdem in die normalen „langen CSS-Selektoren“ kompilieren.

Mit den CSS Custom Properties (Variablen) und der Verschachtelung sind nun zwei von drei Funktionen nativ in CSS enthalten, die ich in Sass häufig verwende. Die letzte ist das „Templating“ mit mehreren Dateien für verschiedene Teile/Komponenten. Der Rest wie Mixins sind mehr „nice to have“ für mich, aber nicht wirklich wichtig.

Dynamische Parameter in JavaScript

Viele Programmiersprachen verfügen über diese Möglichkeit. Anstatt alle Parameter für eine Funktion zu definieren, können ihr eine beliebige Anzahl von Parametern an eine Funktion übergeben und dann mit ihnen arbeiten.

Funktionen mit statischen Parametern

Bevor wir uns mit dynamischen Parametern beschäftigen, führen wir erst einmal eine Funktion ein, mit der wir dann anschließend arbeiten können. Wie wäre es mit einer, die zwei Zahlen addiert?

function sum( a, b ) {
    return a + b;
}
 
console.log( sum( 4, 5 ) ); // 9

Nichts Besonderes hier, oder? Aber was würde passieren, wenn wir dem Funktionsaufruf einfach weitere Parameter hinzufügen?

console.log( sum( 4, 5, 6 ) ); // 9

Es würde nichts passieren, da die Funktion nur die ersten beiden Parameter verwendet. Wir erhalten aber auch keine Fehlermeldung. JavaScript verwendet den dritten Parameter einfach nicht.

Das arguments Objekt

Auch wenn wir den dritten Parameter nicht verwenden, ist er immer noch verfügbar. Aber wie können wir ihn verwenden, wenn wir keinen Parameternamen für ihn haben? Wir können das arguments Objekt verwenden, um alle Parameter zu erhalten, selbst dann, wenn wir überhaupt keine Parameter definiert haben:

function sum() {
  let total = 0;
  for (const number of arguments) {
    total += number;
  }
  return total;
}
 
console.log( sum( 4, 5 ) ); // 9
console.log( sum( 4, 5, 6 ) ); // 15

Das arguments Objekt funktioniert ähnlich wie ein Array. Ihr können mit arguments[0] auf den ersten Parameter zugreifen usw. In unserer Funktion sum() haben wir überhaupt keine Argumente, wie zuvor erwähnt. Aber man kann auch definierte Parameter mit zusätzlichen kombinieren:

function someFunction( a, b ) {
    console.log( a ); // 4
    console.log( b ); // 5
    console.log( arguments ); // [4, 5, 'a string', Array(2), ...]
}
 
someFunction( 4, 5, 'a string', [ 'an', 'array' ] );

Das arguments-Objekt enthält immer alle Parameter, die an die Funktion übergeben werden, auch solche mit einem Namen. Das Objekt hat noch einige andere Eigenschaften, über die ihr in der Dokumentation mehr erfahren könnt.

Der Rest-Parameter

Eine modernere Art, dynamische Parameter in JavaScript zu verwenden, ist der Rest -Parameter. Lasst uns wieder mit einem einfachen Beispiel beginnen. Wir implementieren unsere Funktion sum() und verwenden dieses Mal den Rest-Parameter:

function sum( ...numbers ) {
    let total = 0;
    for ( const number of numbers ) {
        total += number;
    }
    return total;
}
 
console.log( sum( 4, 5 ) ); // 9
console.log( sum( 4, 5, 6 ) ); // 15

Der einzige Parameter ist hier „...numbers“ und das ist der „Rest-Parameter“. Ein weiteres gutes Beispiel ist dieses:

function calc( operation, ...numbers ) {
    switch ( operation ) {
        case "sum":
            return numbers.reduce( ( total, num ) => total + num, 0 );
        case "multiply":
            return numbers.reduce( ( product, num ) => product * num, 1 );
        case "subtract":
            return numbers.reduce( ( difference, num ) => difference - num );
        case "divide":
            return numbers.reduce( ( quotient, num ) => quotient / num );
        default:
            console.error(`Invalid operation "${operation}"`);
            return;
    }
}
 
console.log( calc( "sum", 4, 5, 6 ) ); // 15
console.log( calc( "multiply", 4, 5, 6  ) ); // 120

Jetzt macht der „Rest“ Sinn, oder? Wir haben einige normale Parameter, gefolgt vom Rest-Parameter, der alle anderen Parameter enthält, eben einfach ausgedrückt, den „Rest“ der Parameter. Es kann immer nur einen Rest-Parameter geben, und dieser muss der letzte Parameter sein. Dieses Beispiel zeigt auch einen guten Anwendungsfall für die Funktion console.error() aus unserem letzten Blogbeitrag.

Bonus: das arguments Objekt verwendet als Rest-Parameter

In unserem ersten Beispiel für den Rest-Parameter bräuchten wir nicht einmal den Parameter ...numbers, da er der einzige Parameter ist. Wir hätten einfach das arguments Objekt in Kombination mit dem Spread-Operator wie folgt verwenden können:

function sum() {
  let total = 0;
  const numbers = [...arguments]; // Convert arguments object to a real array
  for (const number of numbers) {
    total += number;
  }
  return total;
}

Diese Umwandlung in Zeile 3 ist eigentlich nicht notwendig, da sich das arguments Objekt sehr ähnlich zu einem Array verhält, aber man kann es trotzdem tun, je nach eigenem Anwendungsfall dafür. Sobald ihr aber mehr (benannte) Parameter habt, würde das wahrscheinlich nicht mehr funktionieren.

Fazit

Eine Funktion mit einer festen Anzahl von Argumenten kann zu unflexibel sein, vor allem, wenn man mit Arrays oder Listen arbeitet. In diesen Fällen kann der Rest-Parameter oder das arguments Objekt helfen, einige schöne und kurze Funktionen zu schreiben.

Fortgeschrittenes JavaScript-Debugging

Im ersten Blogbeitrag des Adventskalenders in diesem Jahr habe ich euch „Erste Schritte beim JavaScript-Debugging“ gezeigt. Heute möchte ich einige fortgeschrittene Techniken vorstellen, die ihr zum Debuggen eures Codes verwenden können.

Verschiedene Logging-Typen

Im ersten Blogbeitrag haben wir nur console.log() verwendet, aber es gibt noch weitere Funktionen des console Objekts, die ihr verwenden könnt:

console.debug('debug message');
console.info('info message');
console.log('log message');
console.error('error message');
console.warn('warn message');

Und so würden sie in der Chrome-Konsole aussehen:

The Chrome Console with the output of the four debugging methods info, log, error and warning.

Die Methoden info und log erzeugen eine ähnliche Ausgabe. Die error und warn Methoden sind jedoch auffälliger gestaltet. Einige von euch haben vielleicht bemerkt, dass wir hier nur 4 Zeilen sehen. Wo ist die Ausgabe für das debug Logging? Diese wird möglicherweise (standardmäßig) in eurem Browser nicht angezeigt. In Chrome müsst ihr auf das Dropdown-Menü „Default levels“ klicken. Hier könnt ihr dann auch „Verbose“ aktivieren, um die debug Nachrichten zu sehen:

The Chrome Console with the output of debug method as well, after activating the "Verbose" debug level.

Da die debug Ausgabe (in Chrome) ebenfalls ähnlich ist, brauchen ihr sie vielleicht nicht wirklich. Ich würde wahrscheinlich hauptsächlich log, warn und error verwenden.

Verwendung von Format-Strings und Styling in Debugging-Ausgaben

Die Debugging-Funktion akzeptiert mehr als einen Parameter. Wenn ihr einfach mehrere Strings, Zahlen, Objekte usw. übergebt, wird jede dieser Parameter in einer Zeile, durch Leerzeichen getrennt (für skalare Typen) oder untereinander (für Objekte) ausgegeben. Ihr können aber auch zusätzliche Parameter für Format-Strings und sogar für die Styling-Angaben verwenden.

Beginnen wir mit einem Format-Strings. Ein Beispiel mit einem einzelnen %s Platzhalter für einen String könnte so aussehen:

const code = 'Code';
console.log('%s is Poetry.', code);

Die Ausgabe wäre Code is Poetry, wie wir es erwarten würden. Neben dem üblichen Format-String-Platzhalter könnt ihr aber auch den %c Platzhalter verwenden. Der nächste Parameter wäre ein CSS-String, mit dem der darauf folgende Text gestylt werden würde. Einfach so:

console.log(
	'This is %csome green text, followed by %csome red and italic text.',
	'color: green;',
	'color: red; font-style: italic;',
);

Wenn ihr mehrere %c Platzhalter habt, wechseln die Styles zum nächsten Parameter und gestalten jeden dann folgenden Text mit diesen neuen Styles. Die Ausgabe des obigen Codes sieht wie folgt aus:

Sie können die %c-Platzhalter auch mit jedem anderen üblichen Platzhalter kombinieren. Probiert es doch einfach mal in eurer aktuellen Browser-Konsole aus. Aber ihr dürft nicht zu verrückt werden. Es gibt nur ein paar CSS-Eigenschaften und -Werte, die ihr in Firefox und Chrome verwenden könnt.

Weitere nützliche Debugging-Funktionen für die Konsole

Wenn ihr viele Meldungen protokollieren möchtet, dann können ihr sie gruppieren. Ihr fangt eine neue Gruppe an, indem ihr die Funktion console.group() verwendet (mit einem optionalen Label) und schließt die Gruppe mit console.groupEnd(). Ihr können sogar Gruppen in Gruppen verschachteln und somit eine Art „Baumstruktur“ erstellen.

Es gibt noch einige andere Instanzmethoden, die für eure Debugging-Aufgaben nützlich sein können, aber eine davon ist wirklich cool!

Zeitmessung in der Konsole

Das console Objekt verfügt über einige Zeitmessfunktionen, die ihr für einfache Performanceanalyse verwenden könnt. Hier ist ein Beispiel, das die Funktion console.time() verwendet:

console.time("a timer label");
slowFunction(1000);
console.timeEnd("a timer label");

function slowFunction(milliseconds) {
	const start = Date.now();
	while (Date.now() - start < milliseconds) {
		// Simulating a long-running function
	}
}

Die Funktion „wartet eine Sekunde lang“ innerhalb der while-Schleife. Die Ausgabe in Chrome sieht wie folgt aus:

a timer label: 999.2578125 ms

Mit dem optionalen „label“ als erstem Parameter können ihr mehrere Timer laufen lassen. Wenn ihr die Zeiten dazwischen messen möchten, können ihr console.timeLog() (mit dem optional passenden Label) verwenden, um „Zwischenzeiten“ zu protokollieren.

Fazit

Beim Entwickeln oder Analysieren von Code kann es sehr hilfreich sein, Dinge in der Browser-Konsole zu protokollieren. Wenn ihr bisher nur console.log() verwendet habt, machen euch mal mit den anderen nützlichen Methoden vertraut.

Ihr solltet aber natürlich immer darauf achten, diese Debugging-Methoden zu entfernen, bevor ihr euren Code fertigstellt. Einige JavaScript-Compiler machen das sogar automatisch, wenn ihr diese für „Production“ bauen lasst.

Das native HTML-Range-Input-Element

Viele von uns haben ihn schon gesehen und verwendet. Im WordPress-Block-Editor ist er für Dinge wie die Schriftgröße verschiedener Blöcke verfügbar. So sieht es z.B. sie für den core/paragraph Block aus:

Die „Typografie“-Einstellungen für die Schriftgröße eines Absatzblocks, mit einer Zahleneingabe auf der linken Seite und einer Bereichseingabe auf der rechten Seite, mit dem numerischen Wert in einem Tooltip darunter.

Wenn ihr den Input verwendet, wird der aktuelle Wert unter dem „Anfasser“ angezeigt und auch im Zahlenfeld links davon aktualisiert.

Optionen für den Input

Die beiden wichtigsten Attribute für diesen Eingabetyp sind wahrscheinlich die Attribute min und max. Hier also ein einfaches Beispiel:

<input type="range" id="font-size" name="font-size" min="16" max="96" />
<label for="font-size">font size</label>

Vergesst wie immer nicht, ein <label> für eure Eingabefelder zu verwenden. Wenn ihr min nicht festlegen, ist der Standardwert 0 und für max ist er 100.

Ein weiteres Attribut, das ihr vielleicht verwenden möchten, ist step. Das kann ein Dezimal- oder Gleitkommawert sein, wie in diesem Beispiel:

<input type="range" id="font-size" name="font-size" min="16" max="96" step="4" />
<label for="font-size">font size</label>
<input type="range" id=">line-height" name="line-height" min="0" max="10" step="0.1" />
<label for="line-height">line height</label>

Aus irgendeinem Grund verwendet WordPress keinen Range-Input, sondern nur ein Zahlenfeld. Aber auch hier wird eine Schrittweite von „0,1“ verwendet.

Auf der Dokumentationsseite finden ihr auch ein ziemlich verrücktes Beispiel für das step Attribut und das sieht so aus:

<input id="pi_input" type="range" min="0" max="3.14" step="any" />

Wenn ihr den Wert „any“ verwendet, nimmt das Input wirklich „jeden beliebigen Fließkommawert“ zwischen min und max an. Interessanterweise erhaltet ihr 100 diskrete Werte, wenn ihr mit der Tastatur zum Feld navigiert und dann mit den Cursortasten links/rechts den Anfasser bewegt. Wenn ihr aber die Maus verwendet, dann erhalten ihr wirklich zufällige und wilde Zahlen dazwischen.

Verwendung des list Attributs

Im letzten Blogbeitrag zum Thema HTML haben wir bereits das list Attribut kennengelernt und auch das Range-Input unterstützt es. Einige normale und kreative Beispiele finden ihr in der Dokumentation. In Chrome lässt die Verwendung eines list Attributs den Anfasser an den <option> Werten der <datalist> „einrasten“. Ihr könnt aber auch einen Wert dazwischen einstellen. Meiner Meinung nach kann man das list Attribute am besten mit step kombinieren. In diesem Fall sind dann nur die Auswahl von Optionen in der <datalist> erlaubt:

<label for="temp">Choose a comfortable temperature:</label><br />
<input type="range" id="temp" name="temp" list="markers" step="25" />

<datalist id="markers">
  <option value="0"></option>
  <option value="25"></option>
  <option value="50"></option>
  <option value="75"></option>
  <option value="100"></option>
</datalist>

Dieser Codeschnipsel zeigt einen Range-Input ohne min/max Werte, aber mit einem zusätzlichen step="25" Attribut. Schauen euch dazu auch das Beispiel mit den Labels an.

Styling des Inputs

Das Styling würde je nach Browser oder Betriebssystem sehr unterschiedlich aussehen. Auch wenn WordPress einen solchen Input verwendet, ist das, was ihr seht, nicht das Input selbst. Es wird „transparent“ stattdessen gemacht und liegt über einem benutzerdefinierten HTML-Element, das mehrere <span> Elemente verwendet, um ein UI-Element zu schaffen, das in allen Browsern gleich aussieht. Wenn ihr genau dieses Element haben möchten, könnt ihr die RangeControl Komponente in eurem JavaScript-Code verwenden.

Aber auch wenn ihr das Range-Input nicht exakt gleich gestalten könnt, gibt es doch einige Optionen, wie etwa für das Ändern der Farbe des Balkens und des Anfassers:

input[type='range'] {
  height: 30px;
}
input[type='range'],
input[type='range']::-webkit-slider-runnable-track,
input[type='range']::-webkit-slider-thumb {
  -webkit-appearance: none;
}
input[type='range']::-webkit-slider-runnable-track {
  height: 10px;
  background: red;
}
input[type='range']::-webkit-slider-thumb {
  position: relative;
  height: 30px;
  width: 30px;
  margin-top: -10px;
  background: green;
  border-radius: 50%;
}

Dies ist nur ein Beispiel für Chrome/Webkit. Es ist wichtig, viele Dinge auf appearance: none zu setzen, da sonst die anderen Styling-Änderungen keine Wirkung haben würden. Diese Stile würden zu dieser Darstellung führen:

A label "Choose a comfortable temperature" with a range input below it. The input has a red background color (the range bar) and a green "thumb" (a circle to change the value).

Ihr wisst alle, dass ich kein Designer bin, aber ich hoffe, dass dies das Styling ein wenig illustriert. Wenn ihr wirklich kreativ werden wollt, schauen euch sich die Blogbeiträge von CSS-Tricks, Smashing Magazine oder W3Schools an.

Fazit

Range-Inputs können die UX einer Online-Anwendung wirklich verbessern, da Zahleneingaben in manchen Fällen nicht so „responsive“ sind. Und wenn ihr nicht in die WordPress-JavaScript-Bibliotheken einsetzt, ist die Verwendung des nativen Range-Inputs vielleicht alles, was ihr braucht.

Smooth Scrolling mit CSS

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. 😉

Labeled Statements in JavaScript

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.

Debugging von Arrays und Objekten in JavaScript mit der console.table() Funktion

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:

const colors = ["red", "green", "blue"];

console.table(colors);

Dies ist ein einfaches Beispiel mit einem eindimensionalen (numerisches) Array. Das Ergebnis in der Browser-Konsole sieht dann so aus:

Browser-Konsole, die eine Tabelle mit „(index)“ als erste Spaltenüberschrift und Werten von 0-2 und „Value“ als zweite Spaltenüberschrift mit den Farbnamen anzeigt.

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:

const names = [
	["Leia", "Organa"],
	["Luke", "Skywalker"],
	["Han", "Solo"],
];

console.table(names);

Jetzt haben wir ein Array von (numerischen) Arrays und so sieht es in der Konsole aus:

(index)01
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)firstNamelastName
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)firstNamelastName
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“.

Implementierung eines Farbwählers mit nativem HTML

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:

<input type="color" id="background" value="#000000" />
<label for="background">Background</label>

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 Farbwähler in Linux
Chrome (Linux)
Chrome Farbwähler in Windows
Chrome (Windows)
Firefox (Linux)
Firefox Farbwähler in Windows
Firefox (Windows)

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:

<input type="color" id="text" list="text-colors"/>
<label for="text">Text</label>
<datalist id="text-colors">
	<option>#ff0000</option>
	<option>#00ff00</option>
	<option>#0000ff</option>
	<option>#ffffff</option>
	<option>#000000</option>
</datalist>

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:

Farbliste in Chrome (Windows)
Farbliste in Chrome (Windows)

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:

ColorPicker-Komponente mit einem Farbwähler, einer Auswahl für „Hex“, einem „Wert kopieren“ Button und einem Input mit „#000000“ als Wert.

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:

import { useState } from 'react';
import { PanelColorSettings } from '@wordpress/block-editor';
import { __ } from '@wordpress/i18n';

export const MyPanelColorSettings = () => {
	const [ textColor, setTextColor ] = useState( { color: '#000' } );
	const [ backgroundColor, setBackgroundColor ] = useState( { color: '#fff' } );

	return (
		<PanelColorSettings
			__experimentalIsRenderedInSidebar
			title={ __( 'Color' ) }
			colorSettings={ [
				{
					value: textColor.color,
					onChange: setTextColor,
					label: __( 'Text' ),
				},
				{
					value: backgroundColor.color,
					onChange: setBackgroundColor,
					label: __( 'Background' ),
				},
			] }
		/>
	);
};

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:

PanelColorSettings zeigt das Fenster zur Auswahl einer Farbe, mit den Farben aus dem Theme und den Standardfarben auf der linken Seite und den Einstellungen für „Text“ und „Hintergrund“ auf der rechten Seite.

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. 😉

Wie man Fehler mit einem „Sticky Header“ behebt

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:

.fixed-header {
    position: fixed;
    top: 0;
    width: 100%;
    z-index: 999;
}
html {
    margin-top: 60px;
}

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.

Spaß mit Zahlen in JavaScript

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:

42 === 42.0 // true
042 === 42 // false
0o42 === 34 // true
042 === 34 // true
099 === 99 // true
0x42 === 66 // true
42 === 0b101010 // true
42 === 0.042e3 // true
42n === 42 // false
42n == 42 // true

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:

Number.isInteger(42) // true
Number.isInteger(42.001) // false
Number.isInteger(42.0) // true
Number.isInteger(42.000000000000001) // true

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:

const number = 1234567.89;
// German
console.log(number.toLocaleString("de")); // 1,234,567.89
// English (United States)
console.log(number.toLocaleString("en-US")); // 1,234,567.89
// Englisch (India)
console.log(number.toLocaleString("en-IN")); // 12,34,567.89
// Ukrainian
console.log(number.toLocaleString("uk")); // 1 234 567,89
// Dzongkha
console.log(number.toLocaleString("dz")); // ༡༢,༣༤,༥༦༧.༨༩

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.