Layout-Instabilität beheben

Eine Anleitung zur Verwendung von WebPageTest zum Identifizieren und Beheben von Problemen mit der Layoutstabilität.

In einem früheren Beitrag habe ich beschrieben, wie sich der Cumulative Layout Shift (CLS) in WebPageTest messen lässt. CLS ist eine Aggregation aller Layoutverschiebungen. Daher dachte ich, es wäre interessant, tiefer einzutauchen und jede einzelne Layoutverschiebung auf einer Seite zu untersuchen, um zu verstehen, was die Instabilität verursachen könnte, und das Problem tatsächlich zu beheben.

Layout Shifts messen

Mit der Layout Instability API können wir eine Liste aller Layoutverschiebungsereignisse auf einer Seite abrufen:

new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(list.getEntries().filter(entry => !entry.hadRecentInput));
  }).observe({type: "layout-shift", buffered: true});
}).then(console.log);

Dadurch wird ein Array von Layoutverschiebungen erzeugt, denen keine Eingabeereignisse vorangegangen sind:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 210.78500000294298,
    "duration": 0,
    "value": 0.0001045969445437389,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

In diesem Beispiel gab es eine einzige sehr kleine Verschiebung von 0,01% bei 210 ms.

Wenn Sie den Zeitpunkt und die Schwere der Änderung kennen, können Sie die Ursache besser eingrenzen. Wir kehren zu WebPageTest zurück, um in einer Laborumgebung weitere Tests durchzuführen.

Layoutverschiebungen in WebPageTest messen

Ähnlich wie bei der Messung von CLS in WebPageTest ist für die Messung einzelner Layout Shifts ein benutzerdefinierter Messwert erforderlich. Glücklicherweise ist der Vorgang jetzt einfacher, da Chrome 77 stabil ist. Die Layout Instability API ist standardmäßig aktiviert. Sie sollten also in der Lage sein, das JS-Snippet auf jeder Website in Chrome 77 auszuführen und sofort Ergebnisse zu erhalten. In WebPageTest können Sie den Standard-Chrome-Browser verwenden und müssen sich nicht um Befehlszeilen-Flags oder die Verwendung von Canary kümmern.

Wir ändern das Skript nun, um einen benutzerdefinierten Messwert für WebPageTest zu erstellen:

[LayoutShifts]
return new Promise(resolve => {
  new PerformanceObserver(list => {
    resolve(JSON.stringify(list.getEntries().filter(entry => !entry.hadRecentInput)));
  }).observe({type: "layout-shift", buffered: true});
});

Das Promise in diesem Skript wird in eine JSON-Darstellung des Arrays aufgelöst, nicht in das Array selbst. Das liegt daran, dass mit benutzerdefinierten Messwerten nur primitive Datentypen wie Strings oder Zahlen erzeugt werden können.

Die Website, die ich für den Test verwenden werde, ist ismyhostfastyet.com. Ich habe sie erstellt, um die Ladeleistung von Webhosts in der Praxis zu vergleichen.

Ursachen von Layoutinstabilität ermitteln

In den Ergebnissen sehen wir, dass der benutzerdefinierte Messwert „LayoutShifts“ diesen Wert hat:

[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3087.2349999990547,
    "duration": 0,
    "value": 0.3422101449275362,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

Zusammenfassend lässt sich sagen, dass es bei 3.087 ms eine einzelne Layoutverschiebung von 34,2% gibt. Um den Schuldigen zu identifizieren, verwenden wir die Filmstreifenansicht von WebPageTest.

Zwei Zellen im Filmstreifen mit Screenshots vor und nach dem Layout-Shift.
Zwei Zellen im Filmstreifen mit Screenshots vor und nach der Layoutverschiebung.

Wenn wir im Filmstreifen zu etwa Sekunde 3 scrollen, sehen wir genau, was die Ursache für die Layoutverschiebung von 34% ist: die farbenfrohe Tabelle. Die Website ruft asynchron eine JSON-Datei ab und rendert sie dann in einer Tabelle. Die Tabelle ist anfangs leer. Wenn Sie also warten, bis sie mit den Ergebnissen gefüllt wird, kommt es zu der Verschiebung.

Webfonts-Header erscheint aus dem Nichts.
Web-Schriftart-Header, der aus dem Nichts erscheint.

Aber das ist noch nicht alles! Die Seite ist nach etwa 4, 3 Sekunden visuell vollständig.Die <h1> der Seite „Is my host fast yet?“ (Ist mein Host schon schnell genug?) wird jedoch erst später angezeigt. Das liegt daran, dass auf der Website eine Webfont verwendet wird und keine Maßnahmen zur Optimierung des Renderings ergriffen wurden. Das Layout scheint sich dabei nicht zu verschieben, aber es ist trotzdem nicht nutzerfreundlich, so lange auf den Titel warten zu müssen.

Layoutinstabilität beheben

Nachdem wir nun wissen, dass die asynchron generierte Tabelle für die Verschiebung von einem Drittel des Viewports verantwortlich ist, können wir das Problem beheben. Wir kennen den Inhalt der Tabelle erst, wenn die JSON-Ergebnisse geladen werden. Wir können die Tabelle aber trotzdem mit Platzhalterdaten füllen, damit das Layout beim Rendern des DOM relativ stabil ist.

Hier ist der Code zum Generieren von Platzhalterdaten:

function getRandomFiller(maxLength) {
  var filler = '█';
  var len = Math.ceil(Math.random() * maxLength);
  return new Array(len).fill(filler).join('');
}

function getRandomDistribution() {
  var fast = Math.random();
  var avg = (1 - fast) * Math.random();
  var slow = 1 - (fast + avg);
  return [fast, avg, slow];
}

// Temporary placeholder data.
window.data = [];
for (var i = 0; i < 36; i++) {
  var [fast, avg, slow] = getRandomDistribution();
  window.data.push({
    platform: getRandomFiller(10),
    client: getRandomFiller(5),
    n: getRandomFiller(1),
    fast,
    avg,
    slow
  });
}
updateResultsTable(sortResults(window.data, 'fast'));

Die Platzhalterdaten werden zufällig generiert, bevor sie sortiert werden. Sie enthält das Zeichen „█“, das eine zufällige Anzahl von Malen wiederholt wird, um visuelle Platzhalter für den Text zu erstellen, sowie eine zufällig generierte Verteilung der drei Hauptwerte. Außerdem habe ich einige Stile hinzugefügt, um alle Farben aus der Tabelle zu entfernen. So wird deutlich, dass die Daten noch nicht vollständig geladen sind.

Die Darstellung der verwendeten Platzhalter spielt für die Layoutstabilität keine Rolle. Die Platzhalter sollen Nutzern versichern, dass Inhalte kommen und die Seite nicht defekt ist.

So sehen die Platzhalter aus, während die JSON-Daten geladen werden:

Die Datentabelle wird mit Platzhalterdaten gerendert.
Die Datentabelle wird mit Platzhalterdaten gerendert.

Das Problem mit Webfonts lässt sich viel einfacher beheben. Da auf der Website Google Fonts verwendet werden, müssen wir nur die Property display=swap in der CSS-Anfrage übergeben. Das ist alles. Die Fonts API fügt den Stil font-display: swap in die Schriftartdeklaration ein, sodass der Browser Text sofort in einer Fallback-Schriftart rendern kann. Hier ist das entsprechende Markup mit dem Fix:

<link href="https://fonts.googleapis.com/css?family=Chivo:900&display=swap" rel="stylesheet">

Optimierungen überprüfen

Nachdem wir die Seite noch einmal in WebPageTest ausgeführt haben, können wir einen Vergleich vor und nach der Optimierung erstellen, um den Unterschied zu visualisieren und den neuen Grad der Layoutinstabilität zu messen:

WebPageTest-Filmstreifen, auf dem beide Websites nebeneinander mit und ohne Layoutoptimierungen geladen werden.
WebPageTest-Filmstreifen, auf dem beide Websites nebeneinander mit und ohne Layoutoptimierungen geladen werden.
[
  {
    "name": "",
    "entryType": "layout-shift",
    "startTime": 3070.9349999997357,
    "duration": 0,
    "value": 0.000050272187989256116,
    "hadRecentInput": false,
    "lastInputTime": 0
  }
]

Laut dem benutzerdefinierten Messwert tritt bei 3.071 ms (ungefähr zur gleichen Zeit wie zuvor) immer noch ein Layout-Shift auf, die Schwere des Shifts ist jedoch viel geringer: 0,005%. Damit kann ich leben.

Aus dem Filmstreifen geht auch hervor, dass die Schriftart <h1> sofort auf eine Systemschriftart zurückgreift, sodass Nutzer sie schneller lesen können.

Fazit

Bei komplexen Websites treten wahrscheinlich viel mehr Layoutverschiebungen auf als in diesem Beispiel. Der Prozess zur Behebung ist jedoch derselbe: Fügen Sie WebPageTest Messwerte für die Layoutinstabilität hinzu, gleichen Sie die Ergebnisse mit dem visuellen Ladestreifen ab, um die Ursachen zu ermitteln, und implementieren Sie eine Korrektur mithilfe von Platzhaltern, um den Bildschirmbereich zu reservieren.

(Noch etwas) Layoutinstabilität bei echten Nutzern messen

Es ist zwar schön, WebPageTest vor und nach einer Optimierung auf einer Seite ausführen zu können und eine Verbesserung eines Messwerts zu sehen, aber was wirklich zählt, ist, dass sich die Nutzerfreundlichkeit tatsächlich verbessert. Ist das nicht der Grund, warum wir die Website überhaupt verbessern wollen?

Es wäre also gut, wenn wir neben unseren herkömmlichen Web-Leistungsmesswerten auch die Layoutinstabilität bei echten Nutzern messen würden. Das ist ein wichtiger Teil des Optimierungs-Feedback-Loops, da wir anhand der Daten aus dem Feld sehen können, wo die Probleme liegen und ob unsere Korrekturen einen positiven Unterschied gemacht haben.

Sie können nicht nur eigene Daten zur Layoutinstabilität erfassen, sondern auch den Chrome UX Report nutzen. Er enthält Daten zum Cumulative Layout Shift, die auf der tatsächlichen Nutzererfahrung auf Millionen von Websites basieren. Damit können Sie herausfinden, wie Sie (oder Ihre Mitbewerber) abschneiden, oder die Layoutinstabilität im Web untersuchen.