Natürliche Sortierung in JavaScript

Wenn ihr in eurem Code nur mit englischen Strings zu tun habt, dann können ihr jetzt aufhören weiter zu lesen. Es sei denn, ihr sind neugierig und wollt etwas Neues lernen, dann bleiben bitte dran. 😉

In Sprachen wie Deutsch gibt es Zeichen, die im englischen Alphabet nicht vorkommen. Manche sehen anderen Zeichen ähnlich, aber manche sind extra. Und im Deutschen haben wir nur 4 davon, andere Sprachen haben viele mehr.

Wie Strings normalerweise sortiert werden

Wenn ihre ein Array von Strings habt, gibt es verschiedene Möglichkeiten, wie sie sortiert werden. Schauen wir uns einige Beispiele an:

console.log(['c', 'b', 'a', 'B', 'A', 'C'].sort())
// (6) ['A', 'B', 'C', 'a', 'b', 'c']

Diejenigen von euch, die schon einmal Code geschrieben und mit einer einfachen Sortierfunktion ein Array von Strings sortiert haben, ist vielleicht aufgefallen, dass viele dieser Funktionen die Reihenfolge der Zeichen in der ASCII-Tabelle verwenden. Deshalb sehen wir zuerst die Großbuchstaben, gefolgt von den Kleinbuchstaben.

Das mag für Englisch genau das sein, was wir erwarten und wollen. Aber was passiert, wenn wir ein paar deutsche Umlaute in das Array einfügen:

console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort())
// (6) ['A', 'B', 'a', 'b', 'Ä', 'ä']

Hm, das große „Ä“ kommt nach allen anderen Kleinbuchstaben, das hätten wir selbst eher nicht so sortiert. Da die Umlaute nicht Teil der ASCII-Tabelle sind (wie die meisten Buchstaben, die nicht in „Basic Latin“ enthalten sind), verwendet JavaScript die UTF-16 Codes, um sie zu sortieren. Diese stellen jedes Zeichen mit einer Zahl dar. Der Buchstabe „Ä“ zum Beispiel hat den numerischen Wert 228, während „z“ den Wert 122 hat, und deshalb ist er wie oben vor dem „Ä“ angeordnet.

Einsatz des Intl.Collator Objekts für die natürliche Sortierung

Die Lösung für dieses Problem ist wirklich sehr einfach. Die sort() Funktion für ein Array kann eine Callback-Funktion als Parameter übergeben bekommen. Dieser Funktion werden zwei Werte des Arrays übergeben. Sie soll eine negative Zahl zurückgeben, wenn der erste Wert kleiner ist als der zweite, eine positive Zahl, wenn das Gegenteil der Fall ist, oder 0, wenn beide den gleichen Wert haben.

Aber wir müssen eine solche Callback-Funktion nicht für jede Sprache selbst schreiben. Stattdessen können wir das Intl.Collator Objekt verwenden. Für unser Beispiel mit den deutschen Umlauten würde der Code wie folgt aussehen:

console.log(['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(new Intl.Collator('de').compare));
// (6) ['a', 'A', 'ä', 'Ä', 'b', 'B']

Jetzt haben wir nicht nur unsere Umlaute neben den ähnlich aussehenden Buchstaben, sondern wir erhalten auch jeden Kleinbuchstaben gefolgt von seinem Großbuchstaben. Das ist auch das, was man von einer „Wörterbuchsortierung“ erwarten würde.

Direkte Verwendung der Vergleichsfunktion

Wie bereits erwähnt, erwartet die Funktion sort() eine Callback-Funktion, der eine negative oder positive Zahl oder eine 0 zurückgibt. Und genau das tut die Funktion auch. Wir können diese Funktion direkt aufrufen (und nicht nur auf ihren Namen übergeben) und zwei Buchstaben zum Vergleich übergeben:

new Intl.Collator('de').compare('ä', 'a')
// 1
new Intl.Collator('de').compare('a', 'ä')
// -1
new Intl.Collator('de').compare('a', 'a')
// 0

Um zu zeigen, dass diese Funktion nicht einfach eine statische Liste von Zahlen für jedes Zeichen verwendet, sondern wirklich nach einer bestimmten Sprache sortiert, habe ich hier ein gutes Beispiel dafür:

new Intl.Collator('de').compare('ä', 'z')
// -1
new Intl.Collator('sv').compare('ä', 'z')
// 1

Die schwedische Sprache, die ich gerade versuche zu lernen, hat auch den Buchstaben „ä“. Er ist jedoch anders sortiert als ein deutscher Umlaut, und man findet Wörter, die mit „ä“ beginnen, in einem schwedischen Wörterbuch fast am Ende (erst danach folgen Wörter, die mit „ö“ beginnen).

Erweiterte Optionen verwenden

Es gibt einige Optionen, die ihre zusätzlich als ein Objekt an die Funktion sort() übergeben könnt, wie in diesem Beispiel, wenn ihr die Großbuchstaben zuerst haben möchtet:

console.log(
	['ä', 'b', 'a', 'B', 'Ä', 'A'].sort(
		new Intl.Collator(
			'de', 
			{
				caseFirst: 'upper'
			}
		).compare
	)
);
// (6) ['A', 'a', 'Ä', 'ä', 'B', 'b']

Weitere Optionen finden ihr auf der Dokumentationsseite oder eine vollständige Liste in der ECMA-Spezifikation. Es gibt sogar einige wilde „Sprachen“ wie"de-u-co-phonebk„, um ein Array so anzuordnen, wie es in einem deutschen Telefonbuch stehen würde – falls ihr wisst, wovon ich rede. 😁

Bonus: Zahlenreihen sortieren

Ich möchte nicht über alle Optionen schreiben, aber eine könnte in eurem Code wirklich hilfreich sein. Wenn ihr schon einmal ein Array mit Zahlen als Strings sortiert habt, dann wissen ihr bestimmt, dass die alphanumerische Sortierung hier nicht funktioniert:

console.log(['1', '2', '11', '101', '3'].sort());
// (5) ['1', '101', '11', '2', '3']

Aber mit der Option numeric erhält man eine Sortierung, die man erwarten würde (der erste Parameter „Sprache“ ist hier vermutlich nicht so wichtig):

console.log(
	['1', '2', '11', '101', '3'].sort(
		new Intl.Collator(
			'en',
			{
				numeric: true
			}
		).compare
	)
);
// (5) ['1', '2', '3', '11', '101']

Genau das, was wir haben wollten. 😊

Fazit

Die Sortierung von Strings nach ihrer natürlichen Reihenfolge in einer bestimmten Sprache kann wirklich einen großen Unterschied in Bezug auf die Nutzbarkeit eures Codes machen (oder sogar dazu führen, dass der Code kaputt ist, wenn falsch sortiert wird). Mit dem Intl.Collator Objekt ist das in JavaScript auch recht einfach möglich. Andere Programmiersprachen bieten oft kein so mächtiges Werkzeug an.

Präferenzen im Browser simulieren

Bevor wir uns mit einigen weiteren Themen im Adventskalenders beschäftigen, müssen wir etwas über eine Browserfunktion erfahren, die wir dazu brauchen werden. Deshalb habe ich die Reihenfolge meiner geplanten Themen zum Debugging ein wenig umgestellt, um dieses Thema früher zu behandeln.

In einigen meiner nächsten Blog-Beiträge werde ich über ein paar CSS-Funktionen sprechen, die bestimmten Personen zugutekommen können, die eure Website besuchen. Vor fast 5 Jahren habe ich in einem Blog-Beitrag beschrieben, wieso man „Bewegungen auf einer Webseite für eine bessere Barrierefreiheit reduzieren“ sollte. In diesem Blogbeitrag habe ich erklärt, wie ihr die Einstellungen in Windows 10 oder Android 9 ändern könnt, um die Präferenz für reduzierte Bewegungen zu simulieren. Aber vielleicht verwendet ihr ein anderes Betriebssystem (wie ich z.B. Manjaro Linux für die Arbeit), bei dem es keine Schnelleinstellung gibt. Oder aber, ihr möchtet die Einstellung schneller umschalten können. Glücklicherweise gibt es bei einigen Browsern einen solchen Umschalter für euch.

Einstellungen in Chrome vornehmen

In Chrome (und anderen auf dem Chromium-Projekt basierenden Browsern wie Edge) finden ihr die meisten Einstellungen, indem ihr auf die drei vertikalen Punkte oben rechts klicken, dann „Weitere Tools“ auswählt und dort die „Rendering“ Einstellungen anklickt:

Screenshot, wie man die "Weitere Tools > Rendering" Einstellungen öffnet.

Dadurch öffnet sich ein weiteres Panel in den Entwicklertools. Wenn ihr ein wenig nach unten scrollt, finden ihr dort diese Einstellungen, mit denen ihr verschiedene Vorlieben simulieren könnt. In diesem Beispiel setze ich prefers-reduced-motion auf reduce:

Screenshot mit den diversen Rendering Einstellungen und der "prefers-reduced-motion" Einstellung geöffnet.

Der einzige Nachteil dieser Einstellung: sobald ihr die Entwicklertools schließt, wird die Einstellung auf ihren Standardwert zurückgesetzt. Wenn ihr also eine Website testen möchten, auf der mehr vertikaler Platz zur Verfügung stehen soll (da die beiden Panels viel Höhe beanspruchen), würde ich vorschlagen, die Entwicklertools in einem separaten Fenster anzuzeigen.

Wir werden diese Einstellungen für die kommenden Themen benötigen. Aber es gibt noch mehr Möglichkeiten, Einstellungen zu simulieren, die das Verhalten und das Aussehen eurer Website verändern. Wenn ihr beispielsweise eure Website schnell mal mit einem dunklen Farbschema anzeigen lassen möchtet, dann müssen ihr nicht euer Betriebssystem auf ein dunkles Farbschema umstellen oder die Rendering-Einstellungen öffnen. Stattdessen finden ihr im Abschnitt „Stile“ eine nette Umschaltfunktion. Klickt einfach auf das „Pinsel“-Symbol und wechselt hier zu dem Farbschema, das ihr testen möchtet.

Screenshot des geöffneten Umschalters für die "prefers-color-scheme" Einstellung.

Leider wird das Farbschema, genau wie beim Rendering-Panel, wieder auf den Standardwert zurückgesetzt, sobald ihr die Entwicklertools schließen.

Einstellungen in Edge vornehmen

Obwohl Edge auf Chromium basiert und viele Entwicklertools genau gleich aussehen, finden ihr in Edge nicht das Panel „Weitere Tools > Rendering“. Stattdessen öffnen ihr hierzu erst die Entwicklertools. Auf Panels wie „Elements“ seht ihr im unteren Teil die „Konsole“ und „Probleme“. Hier klickt ihr dann auf das „+“-Symbol und wählt die „Rendering Schnellansicht“. Nach dem Öffnen sieht sie den Einstellungen in Chrome sehr ähnlich.

Screenshot des geöffneten "Tools zur Schnellansicht hinzufügen" Panels in Edge.

Auch in Edge können ihr das Farbschema mit einem „Pinsel“-Symbol schnell umschalten, genau wie in Chrome.

Einstellungen in Firefox vornehmen

In Firefox sind die Einstellungen nicht leicht zugänglich. Außerdem befinden sie sich an verschiedenen Stellen. Wenn ihr prefers-reduced-motion einstellen möchtet, müsst ihr zur URL about:config navigieren, dann nach ui.prefersReducedMotion suchen, einen booleschen Wert hinzufügen und ihn auf false setzen. Für prefers-reduced-transparency müsste ihr hingegen nach layout.css.prefers-reduced-transparency.enabled suchen und diesen Wert auf true setzen. Das ist alles sehr inkonsistent und ärgerlicherweise nicht an einem Ort.

Wenn wir das Farbschema umschalten wollen, haben wir mehr Glück. Firefox bietet zwei Schaltflächen an. Mit dem „Sonnensymbol“ können ihr den hellen Farbmodus und mit dem „Mondsichelsymbol“ den dunklen Farbmodus erzwingen.

Screenshots des Buttons zur Simulation des dunkeln Farbschemas in Firefox.

Wenn ihr eine der beiden Schaltflächen aktiviert, wird automatisch die andere deaktiviert. Wenn ihr beide deaktiviert, wird wider der Standardmodus verwendet.

Bonus: schnelleres Umschalten der Einstellungen in Chromium-Browsern

Ich kann euch leider nicht sagen, wie ihr die Einstellungen in Safari ändern könnt, da ich keinen Mac benutze (oder Safari nicht oft genug simuliere). Aber ich habe einen netten kleinen Bonus-Tipp für euch. Wenn ihr eine dieser Einstellungen schnell ändern möchtet, können ihr die Funktion „Befehl ausführen“ in Chromium-Browsern verwenden.

Drücken hierzu bei geöffneten Entwicklertools die Tastenkombination „Strg + Umschalt + P“ (oder „Cmd + Umschalt + P“ auf einem Mac) und sucht dann nach der Einstellung, die ihr umschalten möchten:

Screenshot vom "Ausführen" Dialog in Chrome.

Ich habe keine entsprechende Funktion in Firefox gefunden, aber vielleicht kennt sie ja jemand von euch?

Fazit

Wenn ihr eine Website entwickelt, die sich je nach Einstellungen des Betriebssystems anders verhält oder anders gerendert wird, sollten ihr dies immer testen. Aber ihr müssen dazu nicht die Einstellungen eures Betriebssystems ändern. Browser haben normalerweise Optionen, um diese zu emulieren.

Wenn jemand von euch bessere Optionen für diese Einstellungen in Firefox oder eine Erweiterung kennt, mit der man diese einfacher umstellen kann, dann teilt den Tipp bitte in den Kommentaren.

Einen Fortschrittsbalken auf eurer Website anzeigen

Wenn Sie eine Webanwendung programmiert, bei der die Anzeige des aktuellen Fortschritts die UX verbessern würde, kann ein netter kleiner Fortschrittsbalken helfen. Und es gibt eine einfache Möglichkeit, einen hinzuzufügen. In der Vergangenheit haben wir dazu etwas wie jQuery UI Progressbar verwendet. Heute können wir jetzt ein natives HTML-Element verwenden.

Das progress HTML-Element

Stellt euch vor, ihr müsst ein Element zur Visualisierung eines Fortschritts auf einer Website erfinden. Wenn ihr überlegt, wie es aussehen würde und welche Attribute es haben sollte, dann kommt ihr vielleicht auf etwas Ähnliches wie das Range-Input, über das ich in einem früheren Beitrag geschrieben habe. Aber das Element ist viel einfacher:

<label for="progress-bar">Progress:</label>
<progress id="progress-bar" value="30" max="100">30 %</progress>

So sieht das native <progress> HTML-Element in Chrome mit dem obigen Code aus:

A label "Progress:" followed by the progress element with a 30% progress.

Wie ihr sehen könnt, ist das Element inline. Und fällt euch etwas auf? Obwohl es sich nicht um ein Formularelement handelt, wurde es mit einem Label kombiniert. Das verbessert seine Barrierefreiheit. Wenn ihr den Wert als Text-Kind-Element hinzufügen, wird dieser Text im Browser nicht angezeigt, wenn das Element unterstützt wird. Es ist also nur notwendig, wenn ihr es mit einem wirklich alten Browser zu tun habt, der das Element nicht unterstützt. Dann wird stattdessen dieser Text inline angezeigt.

Fortgeschrittene Verwendung des Elements

Ist euch aufgefallen, dass wir in dem Beispiel kein min Attribut gesetzt haben? Dieses Attribut ist nicht einmal verfügbar, denn der Standardwert für min ist immer 0.

Aber wussten ihr auch, dass ihr sogar auf das value Attribut verzichten könnt und was dann passieren würde? Vermutlich würde man nicht sofort vermuten, dass das hier passiert:

Ganz richtig! Ihr würdt einen animierten Fortschrittsbalken erhalten. Kein JavaScript, keine CSS-Animation, nur ein <progress> Element ohne Wert. Und um es noch toller zu machen, könnt ihr es mit einigen zusätzlichen Attributen zu einer barrierefreien Fortschrittsanzeige für einen anderen Bereich der Website machen. Hier ist ein Beispiel aus der Dokumentationsseite:

<div aria-busy="true" aria-describedby="progress-bar">
  <!-- content is for this region is loading -->
</div>

<!-- ... -->

<progress id="progress-bar" aria-label="Content loading…"></progress>

Wenn ihr in einem WordPress-Projekt seid, können ihr auch die ProgressBar-Komponente verwenden. Im WordPress-Core scheint sie nur für einen Fortschrittsbalken mit „Ladeanimation“, wie oben gezeigt, verwendet zu werden. Sie verwendet zwar ebenfalls das <progress> Element, aber auch hier wird die Standarddarstellung des Browsers durch benutzerdefiniertes HTML-Markup ersetzt, damit sie in allen Browsern das gleiche Design hat.

Fazit

Ein Fortschrittsbalken kann die UX einer Webanwendung wirklich verbessern. Wenn ihr einen braucht, sollten ihr euch für das native Element entscheiden. Ihr könnt es auch ein wenig stylen. Mehr dazu finden ihr auf den verlinkten Dokumentationsseiten. Wenn ihr ein <progress> Element hinzufügen, achten bitte darauf, dass es auch ein Label bekommt. Das hilft nicht nur Nutzern mit assistiven Technologien. Die Angabe, wofür der Fortschritt steht, hilft allen das Element besser zu verstehen.

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