UI-Komponenten wie Spinner und Skeleton-Loader machen das Warten auf das Laden einer Seite weniger frustrierend und können sogar beeinflussen, wie Ladezeiten wahrgenommen werden, wenn sie korrekt eingesetzt werden. Sie werden Nutzer nicht vollständig davon abhalten, die Website zu verlassen, aber sie könnten sie ermutigen, etwas länger zu warten. Animierte Spinner werden in den meisten Fällen verwendet, da sie einfach zu implementieren sind und im Allgemeinen eine gute Arbeit leisten. Skeleton-Loader haben einen begrenzten Anwendungsbereich und können komplex in der Implementierung und Wartung sein, bieten aber für diese spezifischen Anwendungsfälle ein verbessertes Ladeerlebnis.
Ich habe festgestellt, dass Entwickler entweder unsicher sind, wann sie Skeleton-Loader zur Verbesserung der UX einsetzen sollen, oder nicht wissen, wie sie die Implementierung angehen sollen. Gängigere Beispiele für Skeleton-Loader im Web sind nicht gerade wiederverwendbar oder skalierbar. Sie sind in der Regel für eine einzelne Komponente maßgeschneidert und können für nichts anderes verwendet werden. Das ist einer der Gründe, warum Entwickler stattdessen reguläre Spinner verwenden und den potenziellen Mehraufwand im Code vermeiden. Sicherlich muss es einen Weg geben, Skeleton-Loader einfacher, wiederverwendbarer und skalierbarer zu implementieren.
Spinner-Elemente und Skeleton-Loader
Ein Spinner (oder eine Fortschrittsleiste)-Element ist das einfachste und wahrscheinlich am häufigsten verwendete Element zur Anzeige eines Ladezustands. Ein Spinner mag besser aussehen als eine leere Seite, aber er wird die Aufmerksamkeit des Nutzers nicht lange fesseln. Spinner teilen dem Nutzer mit, dass etwas irgendwann geladen wird. Nutzer müssen passiv warten, bis Inhalte geladen sind, was bedeutet, dass sie nicht mit anderen Elementen auf der Seite interagieren oder andere Inhalte auf der Seite konsumieren können. Spinner nehmen den gesamten Bildschirm ein und kein Inhalt ist für den Nutzer verfügbar.

Jedoch teilen Skeleton-Loader (oder Skeleton-Screens) dem Nutzer mit, dass der Inhalt bald geladen wird und sie bieten ein besseres Ladeerlebnis als ein einfacher Spinner. Leere Boxen (mit einer einfarbigen oder gradienten Hintergrundfarbe) werden als Platzhalter für die geladenen Inhalte verwendet. In den meisten Fällen werden Inhalte schrittweise geladen, was es den Nutzern ermöglicht, ein Fortschrittsgefühl zu bewahren und den Eindruck zu erwecken, dass das Laden einer Seite schneller ist, als es tatsächlich ist. Nutzer warten aktiv, was bedeutet, dass sie mit der Seite interagieren oder zumindest einen Teil der Inhalte konsumieren können, während der Rest geladen wird.

Es ist wichtig zu beachten, dass Ladekomponenten nicht zur Behebung von Leistungsproblemen verwendet werden sollten. Wenn eine Website Leistungsprobleme hat, die behoben werden können (unoptimierte Assets oder Code, Backend-Leistungsprobleme usw.), sollten diese zuerst behoben werden. Ladeelemente werden nicht verhindern, dass Nutzer Websites mit schlechter Leistung und langen Ladezeiten verlassen. Ladeelemente sollten als letztes Mittel eingesetzt werden, wenn Warten unvermeidlich ist und wenn die Ladeverzögerung nicht durch ungelöste Leistungsprobleme verursacht wird.
Skeleton-Loader richtig verwenden
Skeleton-Loader sollten nicht als Ersatz für Vollbild-Ladeelemente betrachtet werden, sondern wenn bestimmte Bedingungen für Inhalt und Layout erfüllt sind. Lassen Sie uns dies Schritt für Schritt betrachten und sehen, wie Lade-UI-Komponenten effektiv eingesetzt werden und wann man stattdessen Skeleton-Loader anstelle von regulären Spinnern wählt.
Ist die Ladeverzögerung vermeidbar?
Der beste Weg, das Laden aus UX-Sicht anzugehen, ist, es ganz zu vermeiden. Wir müssen sicherstellen, dass die Ladeverzögerung unvermeidlich ist und nicht das Ergebnis der oben genannten, behebaren Leistungsprobleme. Die Hauptpriorität sollten immer Leistungsverbesserungen und die Reduzierung der Zeit sein, die zum Abrufen und Anzeigen von Inhalten benötigt wird.
Wird das Laden vom Benutzer initiiert und ist Feedback erforderlich?
In einigen Fällen können Benutzeraktionen dazu führen, dass zusätzliche Inhalte geladen werden. Einige Beispiele sind das Lazy-Loading von Inhalten (z. B. Bilder) im Viewport des Benutzers beim Scrollen, das Laden von Inhalten beim Klicken auf eine Schaltfläche usw. Wir müssen ein Ladeelement einfügen für Fälle, in denen ein Benutzer eine Art von Feedback für seine Aktionen benötigt, die den Ladevorgang ausgelöst haben.
Wie im folgenden Mockup zu sehen ist, weiß ein Benutzer ohne ein Ladeelement zur Bereitstellung von Feedback nicht, dass seine Aktionen einen Ladevorgang ausgelöst haben, der im Hintergrund stattfindet.

Ist das Layout konsistent und vorhersehbar?
Wenn wir uns für ein Ladeelement entschieden haben, müssen wir nun wählen, welcher Ladeelementtyp am besten zu unserem Anwendungsfall passt. Skeleton-Loader sind am effektivsten, wenn wir den Typ und das Layout der geladenen Inhalte vorhersagen können. Wenn das Layout des Skeleton-Loaders das Layout des geladenen Inhalts nicht zu einem gewissen Grad genau darstellt, kann die plötzliche Änderung zu einem Layout-Shift führen und den Nutzer verwirren und desorientieren. Verwenden Sie Skeleton-Loader für Elemente mit vorhersehbarem Inhalt für konsistente Layouts.

Gibt es auf der Seite Inhalte, die dem Nutzer sofort zur Verfügung stehen?
Skeleton-Loader sind am effektivsten, wenn beim aktiven Skeleton-Loader Abschnitte oder Seitenelemente bereits auf der Seite vorhanden sind und zusätzliche Inhalte aktiv geladen werden. Das schrittweise Laden von Inhalten bedeutet, dass statische Inhalte beim Laden der Seite verfügbar sind und asynchron geladene Inhalte angezeigt werden, sobald sie verfügbar sind (z. B. zuerst der Text und danach die Bilder). Dieser Ansatz stellt sicher, dass der Benutzer ein Fortschrittsgefühl beibehält und erwartet, dass die Inhalte jederzeit fertig geladen sind. Wenn der gesamte Bildschirm mit Skeleton-Loadern bedeckt ist, ohne dass Inhalte vorhanden sind und ohne schrittweises Laden, ist dies nicht wesentlich besser als ein Vollbild-Spinner oder eine Fortschrittsleiste.

Robuste Skeleton-Loader erstellen
Nachdem wir nun wissen, wann wir Skeleton-Loader verwenden und wie wir sie richtig einsetzen, können wir endlich mit dem Coden beginnen! Aber zuerst möchte ich Ihnen sagen, wie wir diesen Ansatz verfolgen werden.
Die meisten Skeleton-Loading-Beispiele aus dem Web sind meiner Meinung nach überkonstruiert und wartungsintensiv. Sie haben vielleicht eines dieser Beispiele gesehen, bei denen Skeleton-Screens als separate UI-Komponente mit separaten CSS-Stilen erstellt werden oder mit aufwendiger Verwendung von CSS-Verläufen erstellt werden, um das endgültige Layout zu simulieren. Das Erstellen und Pflegen eines separaten Skeleton-Loaders oder von Skeleton-Stilen für jede UI-Komponente kann mit einem solch hochspezifischen Ansatz zu einem erheblichen Entwicklungsaufwand werden. Dies gilt insbesondere, wenn wir die Skalierbarkeit betrachten, da jede Änderung am bestehenden Layout auch die Aktualisierung des Skeleton-Layouts oder der Stile mit sich bringt.
Lassen Sie uns einen rudimentären Ansatz zur Implementierung von Skeleton-Loading finden, der für die meisten Anwendungsfälle funktionieren sollte und einfach zu implementieren, wiederzuverwenden und zu warten ist!
Card-Grid-Komponente
Wir werden reguläres HTML, CSS und JavaScript für die Implementierung verwenden, aber der allgemeine Ansatz kann angepasst werden, um mit den meisten Tech-Stacks und Frameworks zu funktionieren.
Wir werden als Beispiel ein einfaches Grid aus sechs Karten-Elementen (drei in jeder Reihe) erstellen und das asynchrone Laden von Inhalten mit einem Button-Klick simulieren.
Wir werden die folgende Markierung für jede Karte verwenden. Beachten Sie, dass wir die Breite und Höhe unserer Bilder festlegen und ein 1px transparentes Bild als Platzhalter verwenden. Dies stellt sicher, dass der Skeleton-Loader für Bilder sichtbar ist, bis das Bild geladen ist.
<div class="card">
<img width="200" height="200" class="card-image" src="..." />
<h3 class="card-title"></h3>
<p class="card-description"></p>
<button class="card-button">Card button</button>
</div>
Hier ist unser Karten-Grid-Beispiel mit einigen Layout- und Präsentationsstilen. Inhaltsknoten werden je nach Ladezustand mit JavaScript zum DOM hinzugefügt oder entfernt, um asynchrones Laden zu simulieren.
Skeleton-Loader-Stile
Entwickler implementieren Skeleton-Loader üblicherweise, indem sie Ersatz-Skeleton-Komponenten (mit dedizierten Skeleton-CSS-Klassen) erstellen oder ganze Layouts mit CSS-Verläufen nachbilden. Diese Ansätze sind unflexibel und überhaupt nicht wiederverwendbar, da individuelle Skeleton-Loader für jedes Layout maßgeschneidert sind. Da Layout-Stile (Abstände, Grid, Inline-, Block- und Flex-Elemente usw.) bereits aus den Hauptkomponenten-Stilen (Karte) vorhanden sind, müssen Skeleton-Loader nur den Inhalt ersetzen, nicht die gesamte Komponente!
Mit diesem Gedanken im Hinterkopf erstellen wir Skeleton-Loader-Stile, die nur dann aktiv werden, wenn eine Elterklasse gesetzt ist, und verwenden CSS-Eigenschaften, die nur die Darstellung und den Inhalt beeinflussen. Beachten Sie, dass diese Stile unabhängig vom Layout und Inhalt des Elements sind, auf das sie angewendet werden, was sie hochgradig wiederverwendbar machen sollte.
.loading .loading-item {
background: #949494 !important; /* Customizable skeleton loader color */
color: rgba(0, 0, 0, 0) !important;
border-color: rgba(0, 0, 0, 0) !important;
user-select: none;
cursor: wait;
}
.loading .loading-item * {
visibility: hidden !important;
}
.loading .loading-item:empty::after,
.loading .loading-item *:empty::after {
content: "\00a0";
}
Die Basis-Elternklasse .loading wird verwendet, um die Skeleton-Loading-Stile zu aktivieren. Die Klasse .loading-item wird verwendet, um die präsentationsbezogenen Stile des Elements zu überschreiben, um ein Skeleton-Element anzuzeigen. Dies stellt auch sicher, dass das Layout und die Abmessungen des Elements erhalten bleiben und vom Skeleton übernommen werden. Zusätzlich stellt .loading-item sicher, dass alle Kindelemente versteckt sind und mindestens ein Leerzeichen (\00a0) enthalten, damit das Element angezeigt wird und sein Layout gerendert wird.
Fügen wir unseren Markups Skeleton-Loader-CSS-Klassen hinzu. Beachten Sie, dass keine zusätzlichen HTML-Elemente hinzugefügt wurden, wir wenden nur zusätzliche CSS-Klassen an.
<div class="card loading">
<img width="200" height="200" class="card-image loading-item" src="..." />
<h3 class="card-title loading-item"></h3>
<p class="card-description loading-item"></p>
<button class="card-button loading-item">Card button</button>
</div>
Sobald der Inhalt geladen ist, müssen wir nur noch die CSS-Klasse loading vom Elternelement entfernen, um die Skeleton-Loader-Stile auszublenden.
Diese wenigen Zeilen sollten für die meisten, wenn nicht alle, Anwendungsfälle funktionieren, abhängig von Ihrem benutzerdefinierten CSS, da diese Skeleton-Loader das Layout von den Standardstilen erben und eine solide Box erstellen, die den Inhalt ersetzt, indem sie den im Layout verbleibenden leeren Raum füllt. Wir wenden diese Klassen auch auf nicht leere Elemente (Button mit Text) an und ersetzen sie durch ein Skeleton. Ein Button mag den Textinhalt von Anfang an bereit haben, ihm fehlen aber möglicherweise zusätzliche Daten, die für seine korrekte Funktion erforderlich sind, daher sollten wir ihn auch ausblenden, bis diese Daten geladen sind.
Dieser Ansatz kann auch an die meisten Änderungen im Layout und in der Markierung angepasst werden. Wenn wir zum Beispiel den Beschreibungs-Teil der Karte entfernen oder den Titel über das Bild verschieben würden, müssten wir keine Änderungen an den Skeleton-Stilen vornehmen, da das Skeleton auf alle Änderungen in der Markierung reagiert.
Zusätzliche Skeleton-Loader-Überschreibungsstile können für ein bestimmtes Element angewendet werden, indem einfach der Selektor .loading .target-element verwendet wird.
.loading .button,
.loading .link {
pointer-events: none;
}
Mehrzeilige Inhalte und Layout-Shifts
Wie Sie sehen können, funktioniert das vorherige Beispiel hervorragend mit Karten und dem von uns verwendeten Grid-Layout, aber beachten Sie, dass der Seiteninhalt beim Laden leicht springt. Dies wird als Layout-Shift bezeichnet. Unsere .card-description Komponente hat eine feste Höhe mit drei Textzeilen, aber der Skeleton-Platzhalter erstreckt sich nur über eine Textzeile. Wenn der zusätzliche Inhalt geladen wird, ändern sich die Container-Dimensionen und das gesamte Layout verschiebt sich als Ergebnis. Ein Layout-Shift ist in diesem speziellen Fall nicht schlecht, kann aber in schwerwiegenderen Fällen den Benutzer verwirren und desorientieren.
Dies kann einfach direkt im Platzhalter-Element behoben werden. Der Platzhalter-Inhalt wird sowieso durch den zu ladenden Inhalt ersetzt, daher können wir darin alles hinzufügen, was wir brauchen. Fügen wir also ein paar <br />-Elemente hinzu, um mehrere Textzeilen zu simulieren.
<div class="card loading">
<img width="200" height="200" class="card-image loading-item" src="..." />
<h3 class="card-title loading-item"></h3>
<p class="card-description loading-item"><br/><br/><br/></p>
<button class="card-button loading-item">Card button</button>
</div>
Wir verwenden einfaches HTML, um das Skeleton zu formen und die Anzahl der Zeilen darin zu ändern. Andere Beispiele im Web erreichen dies möglicherweise mit CSS-Padding oder auf andere Weise, aber dies führt zu einem Mehraufwand im Code. Schließlich kann der Inhalt eine beliebige Anzahl von Zeilen umfassen und wir möchten all diese Fälle abdecken.
Als zusätzlicher Vorteil der Verwendung von <br />-Elementen erben diese die CSS-Eigenschaften, die die Inhaltsabmessungen beeinflussen (z. B. die Zeilenhöhe, Schriftgröße usw.). Ebenso können  -Zeichen verwendet werden, um zusätzliche Abstände zu den Inline-Platzhalter-Elementen hinzuzufügen.
Mit wenigen Zeilen CSS haben wir vielseitige und erweiterbare Skeleton-Loader-Stile erstellt, die auf eine breite Palette von UI-Komponenten angewendet werden können. Wir haben auch einen einfachen Weg gefunden, die Skeleton-Boxen vertikal zu erweitern, um Inhalte zu simulieren, die sich über mehrere Textzeilen erstrecken.
Um zu zeigen, wie vielseitig dieser Skeleton-Loader-CSS-Schnipsel ist, habe ich ein einfaches Beispiel erstellt, bei dem ich den Schnipsel zu einer Seite mit dem Bootstrap CSS Framework hinzugefügt habe, ohne zusätzliche Änderungen oder Überschreibungen. Bitte beachten Sie, dass in diesem Beispiel keine Textinhalte angezeigt oder simuliert werden, aber er funktioniert wie in den vorherigen Beispielen. Dies dient nur zur Veranschaulichung, wie Stile leicht mit anderen CSS-Systemen integriert werden können.
Hier ist ein zusätzliches Beispiel, das zeigt, wie diese Stile auf verschiedene Elemente angewendet werden können, einschließlich input-, label- und a-Elementen.
Zugänglichkeitsanforderungen
Wir sollten auch die Anforderungen an die Zugänglichkeit (a11y) berücksichtigen und sicherstellen, dass die Inhalte für alle Benutzer zugänglich sind. Skeleton-Loader ohne A11y-Funktionen können Benutzer mit visuellen Einschränkungen oder Benutzer, die das Web mit Screenreadern durchsuchen, desorientieren und verwirren.
Kontrast
Sie haben vielleicht bemerkt, dass die Skeleton-Loader in unserem Beispiel einen hohen Kontrast haben und auffälliger sind als die üblichen Low-Contrast-Skeleton-Loader im Internet. Einige Benutzer haben möglicherweise Schwierigkeiten, Low-Contrast-UI-Komponenten wahrzunehmen und zu verwenden. Deshalb geben die Web Content Accessibility Guidelines (WCAG) einen Mindestkontrast von 3:1 für Nicht-Text-UI-Komponenten vor.
Der kommende Entwurf "Media Queries Level 5" enthält eine prefers-contrast Media Query, die es uns ermöglicht, die Kontrastpräferenzen des Benutzers zu erkennen. Dies gibt uns mehr Flexibilität, indem es uns erlaubt, für Benutzer, die eine kontrastreiche Version anfordern, eine kontrastreiche Hintergrundfarbe für Skeleton-Loader zuzuweisen und für andere eine subtile Low-Contrast-Hintergrundfarbe zu verwenden. Ich würde empfehlen, standardmäßig kontrastreiche Skeleton-Loader zu implementieren, bis die prefers-contrast Media Query weiter verbreitet ist.
/* NOTE: as of the time of writing this article, this feature is not supported in browsers, so this code won't work */
.loading .loading-item {
/* Default skeleton loader styles */
}
@media (prefers-contrast: high) {
.loading .loading-item {
/* High-contrast skeleton loader styles */
}
}
Animationen
Je nach Design und Implementierung von animierten Skeleton-Loadern können Benutzer mit visuellen Störungen von den Animationen überfordert sein und die Website als unbenutzbar empfinden. Es ist immer eine gute Idee, Animationen für Benutzer zu verhindern, die reduzierte Bewegung bevorzugen. Diese Media Query wird in modernen Browsern breit unterstützt und kann ohne Vorbehalte verwendet werden.
.loading .loading-item {
animation-name: skeleton;
background: /* animated gradient background */;
}
@media (prefers-reduced-motion) {
.loading .loading-item {
animation: none !important;
background: /* solid color */;
}
}
Screenreader
Um Screenreader besser zu unterstützen, müssen wir unsere HTML mit ARIA (Accessible Rich Internet Applications) Markup aktualisieren. Dieses Markup wirkt sich nicht auf unseren Inhalt oder unsere Darstellung aus, ermöglicht es aber Benutzern von Screenreadern, unsere Website-Inhalte, einschließlich unserer Skeleton-Loader, besser zu verstehen und zu navigieren.
Adrian Roselli hat eine sehr detaillierte Untersuchung zum Thema zugängliche Skeleton-Loader für Fälle, in denen Skeleton-Loader als separate UI-Komponenten implementiert werden. Für unser Beispiel verwende ich das aria-hidden Attribut in Kombination mit visuell verstecktem Text, um Screenreadern einen Hinweis zu geben, dass der Inhalt gerade geladen wird. Screenreader ignorieren Inhalte mit aria-hidden="true", verwenden aber das visually-hidden-Element, um den Ladezustand dem Benutzer anzuzeigen.
Aktualisieren wir unsere Karten mit der ARIA-Markierung und dem Ladeanzeige-Element.
<div class="card loading">
<span aria-hidden="false" class="visually-hidden loading-text">Loading... Please wait.</span>
<img width="200" height="200" class="card-image loading-item" aria-hidden="true" src="..." />
<h3 class="card-title loading-item" aria-hidden="true"></h3>
<p class="card-description loading-item" aria-hidden="true"><br/><br/><br/></p>
<button class="card-button loading-item" aria-hidden="true">Card button</button>
</div>
Wir hätten auch aria-hidden auf das Grid-Container-Element anwenden und ein einzelnes visuell verstecktes Element vor dem Container-Markup hinzufügen können, aber ich wollte die Markup-Beispiele auf ein einzelnes Kartenelement konzentrieren und nicht auf das gesamte Grid, daher habe ich mich für diese Version entschieden.
Wenn der Inhalt geladen und im DOM angezeigt wird, müssen wir aria-hidden für die Inhaltscontainer auf false und aria-hidden auf dem visuell versteckten Lade-Textindikator auf true setzen.
Hier ist das fertige Beispiel
Das war's!
Die Implementierung von Skeleton-Loadern erfordert einen etwas anderen Ansatz als die Implementierung von regulären Ladeelementen wie Spinnern. Ich habe zahlreiche Beispiele im Web gesehen, die Skeleton-Loader so implementieren, dass ihre Wiederverwendbarkeit stark eingeschränkt wird. Diese überkonstruierten Lösungen beinhalten in der Regel die Erstellung separater Skeleton-Loader-UI-Komponenten mit dedizierter (enger Geltungsbereich) Skeleton-CSS-Markierung oder die Nachbildung des Layouts mit CSS-Verläufen und magischen Zahlen. Wir haben gesehen, dass nur der Inhalt durch die Skeleton-Loader ersetzt werden muss und nicht die gesamte Komponente.
Wir haben es geschafft, einfache, vielseitige und wiederverwendbare Skeleton-Loader zu erstellen, die das Layout von den Standardstilen erben und den Inhalt in leeren Containern durch solide Boxen ersetzen. Mit nur zwei CSS-Klassen können diese Skeleton-Loader problemlos zu nahezu jedem HTML-Element hinzugefügt und bei Bedarf erweitert werden. Wir haben auch sichergestellt, dass diese Lösung zugänglich ist und die Markierung nicht mit zusätzlichen HTML-Elementen oder duplizierten UI-Komponenten aufbläht.
Vielen Dank, dass Sie sich die Zeit genommen haben, diesen Artikel zu lesen. Lassen Sie mich Ihre Gedanken zu diesem Ansatz wissen und wie Sie in Ihren Projekten Skeleton-Loader erstellt haben.
Ein guter Artikel!
Wenn ich einen Vorschlag machen darf, wäre es vielleicht besser, wenn die "Lädt… Bitte warten."-Span einmal oben erscheint, anstatt in jeder Karte.
Hallo Glenn, danke. Freut mich, dass Ihnen der Artikel gefällt.
Zu Ihrem Vorschlag stimme ich zu, dass es besser sein könnte, eine einzelne Lade-a11y-Span-Element zu haben. Für diesen Artikel wollte ich, dass alle HTML-Snippets sich auf die Änderungen für eine einzelne Karte konzentrieren.
Hallo
Ich benutze denselben Code, aber meine Elemente werden nie angezeigt
Können Sie einen CodePen-Link beifügen? Außerdem müssen Sie sicherstellen, dass das Elternelement die Klasse "loading" zugewiesen hat.