Was ist ein Cumulative Layout Shift ?

Der Cumulative Layout Shift (CLS) ist ein Maß für die visuelle Stabilität beim Laden einer Seite sowie für unerwartete Änderungen auf der Seite beim Ladevorgang. Google versteht darunter, dass beim Laden der Seite keine größeren Sprünge im Layout der Seite erfolgen sollen, die den User aus seinem aktuellen Fokus reißen.

Zum Beispiel nimmt ein Bild erst nach dem Laden seine Größe in der Seite ein und verdrängt weiter unten liegende Elemente. Setzt der User seinen Fokus beim Starten der Seite zum Beispiel auf einen Text unterhalb eines Bildes und fängt an zu lesen, würde der Text eventuell sogar ganz aus dem Viewport verschoben werden, wenn das Bild über dem Text erst während des Ladens den benötigten Raum einnimmt. Das führt immer wieder zu Verärgerung der User.

Diese User Experience will Google verbessern, indem es den CLS als Rankingfaktor aufnimmt. Je weniger die Elemente der Seite sich während des Ladevorgangs in ihrer Größe verändern, desto geringer ist der CLS.

Wie kann man einen CLS verhindern ?

Für Bilder ist die Herangehensweise vergleichsmäßig einfach wenn man schon im Img-Tag die Breite und Höhe des zu ladenden Bildes angibt. Der Browser hält dann den benötigten Platz frei und es kommt später nicht zu einem Springen des Layouts wenn das Bild geladen ist.
Dieses Verfahren ist jedoch nur bedingt einsetzbar, denn nicht immer weiß man wie groß ein Bild wird, wenn dynamisch Content nachgeladen oder von Redakteuren in einem CMS beliebig große Bilder eingefügt werden können.

Noch schwieriger wird es wenn Texte dargestellt werden, die sich zur Laufzeit ändern.
Gibt es dann auch noch dynamisch angepasste Kategorien in einem Navigationselement ist eine Größenbestimmung des Elements erst kurz vor dem Rendern möglich.

In unserem Shop gibt es diverse Elemente deren Ausdehnung erst kurz vor dem Rendern feststehen. Um einen möglichst geringen CLS zu erzeugen sollen Platzhalter eingefügt werden, die während der Ladezeit durch den letztendlich erzeugten Inhalt ersetzt werden.
Das bedingt den Einsatz von JavaScript und CSS.

Uns stellten sich daher die Fragen:

  • Können wir überhaupt die Platzhalter durch unsere Komponenten erzeugen und ersetzen oder bestimmte CSS-Klassen nachträglich ins DOM schreiben lassen, um ein automatisiertes Verfahren zur Vermeidung eines CLS einsetzen zu können?
  • Können wir die Inhalte der Komponenten beim Laden analysieren und ggf. Platzhalter oder CSS-Klassen einsetzen, bevor Google den CLS berechnet?

Leider gibt Google nicht genau bekannt zu welchem Zeitpunkt der CLS berechnet wird und auf welcher Basis dies geschieht. Daher haben wir uns entschlossen mittels Messreihen mehr Klarheit über Googles CLS-Bestimmung zu erhalten.

Im ersten Schritt schauten wir und den CLS beim Laden unseres Shops an:

Der Messaufbau

Die Messung erfolgte an unserer manipulierten Startseite. Alle Skripte und CSS Ressourcen wurden weiterhin geladen. Die anzuzeigenden Komponenten und DOM-Elemente wurden herausgenommen und durch drei standardisierte div-Container ersetzt.
Da wir nur wissen wollten ob ein CLS bei bestimmten Situationen überhaupt eintritt, war für uns der absolute CLS-Wert nicht wichtig. Daher ist die genaue Größe der Layout Änderungen während des Ladevorgangs nicht ausschlaggebend, sie müssen lt. Google nur signifikant sein.
Eine Größenänderung um 200px in der Höhe schien uns signifikant genug, was spätere Messungen bestätigten.

Das grüne Element wurde durch ein Skript oder durch CSS in der Höhe um 200px verändert. Darüber hinaus wurde die Höhe des Containers auch einmal um einen Wert größer als die aktuelle Viewport-Höhe verändert. Die Container Test2-fix und Test3-fix blieben unverändert. Mal befand sich das Element im Viewport, mal außerhalb oder halb drin.

Es wurden unterschiedliche Szenarien durchgemessen:

  • Wir veränderten die Stelle an der das Skript oder das CSS eingesetzt wird.
  • Das Skript wurde durch setTimeout() mit unterschiedlichen Zeiten verzögert oder direkt ausgeführt.
  • Das CSS oder das Skript wurde direkt ins DOM gesetzt oder als Ressource nachgeladen.
  • Da es wichtig ist, ob ein Element andere Elemente verschiebt oder nicht,
    wurden die Messungen jeweils auch ohne den nachfolgenden Container (Test3-fix) durchgeführt.

Die Messung des CLS erfolgte lokal immer unter gleichen Bedingungen durch die Chrome-Devtools mit Lighthouse.
Die Messungen wurden in Chrome im Mobile-Modus mit einem simulierten Motorola G4 durchgeführt, weil auch Google intern auf einem Moto G4 misst.

Die Messergebnisse – Auszug

Die Tabelle ist dreigeteilt. Im oberen Abschnitt wurde der CLS durch ein Skript erzeugt, im Unteren durch CSS.

Dazwischen liegt eine Messung die für uns besonders wichtig war: Hier wurde ein CLS durch eine Komponente erzeugt, deren JavaScript direkt im Initialisierungsprozess unseres Main-Scripts ausgeführt wurde.

In der Spalte „Position“ ist vermerkt wo das Skript/CSS eingefügt wurde.
Unter „Verzögert“ sieht man, ob das Skript bzw. das CSS zeitverzögert nachgeladen wurde; ein X bedeutet dass es zwar eine Verzögerung gab, die aber nicht genau ermittelt werden konnte.

Interpretation der Messergebnisse

  • Elemente die außerhalb des Viewports liegen oder deren Größenänderungen außerhalb des Viewports stattfinden werden nicht als CLS bewertet.
  • Ein CLS-Wert wird bei einer Höhenänderung nur dann vergeben, wenn im DOM nachfolgende Elemente verschoben werden. Dabei ist es unerheblich wie die Höhenänderung erzeugt wird und wie groß sie ist.
  • Der absolute Wert der Größenänderung während des Ladens ist nicht ausschlaggebend dafür, ob ein CLS gemessen wird oder nicht. Er ist aber proportional zum Wert des CLS selbst. (Genaue Berechnung s. WebDev-CLS)
  • Es gibt keinen Schwellenwert für eine Größenänderung ab dem der CLS nicht erkannt wird.
  • Direkt inline ausgeführtes JavaScript führt zu keinem CLS, solange es nicht direkt vor dem schließenden Body-Tag liegt. Dabei ist die Verarbeitungsdauer des Scripts nicht relevant.
  • Per setTimeout() verzögerte Skripte führen nur solange zu keinem CLS als dass sie vor contentLoaded (nicht DOM-Ready!) fertig sind.
  • Änderungen der Größe eines Elements die durch eine CSS Anweisung erfolgen, erzeugen keinen CLS, solange das CSS nicht vor dem schließenden Body-Tag liegt.
  • Ein per style-Tag nachgeladenes Stylesheet führt nicht zu einem CLS. Interessant! Auch wenn ein Stylesheet künstlich verzögert geladen wird, erfolgt kein CLS durch eine der Anweisungen im Stylesheet.

Fazit für das initiale Laden einer Page

Wir können leider die Verarbeitung von Platzhaltern und das Abfedern von Größenänderungen nicht in unsere Komponenten zur Automatisierung verlagern, weil jedes JavaScript zu einem CLS führt, das über einen script-Tag im Header oder vor dem schließenden body-Tag eingebunden ist und Änderungen am DOM nach DOM-Ready vornimmt.
Google lässt aber scheinbar ein kleines Zeitfenster offen in dem man DOM-Manipulationen machen kann ohne einen CLS zu provozieren. (Dies sind nicht die 500ms die weiter unten beschrieben werden.)

Inline eingebundene Skripte können während des Ladens das DOM manipulieren ohne einen CLS zu erzeugen. Somit könnte man das jeweilige Element analysieren und ggf. Platzhalter oder style-Klassen setzen.
Man nimmt aber dann alle Nachteile eines inline eingebundenen Skripts in Kauf.

CSS-Stylesheets erzeugen nach unseren Messungen kein CLS, was unterschiedliche Positionierungen ganzer Bereiche der Seite je nach Breakpoint möglich macht (Intrinsic Web Design).

Grenzen der Messungen

Wir haben hier nur ein einzelnes Element betrachtet, der CLS berechnet sich aber kumulativ über alle Elemente.
Für die Aussage, ob und wann ein CLS erzeugt wird, kann das vernachlässigt werden, nicht jedoch für den Absolutwert des CLS.
Ob ein CLS Wert, der sich aus vielen einzelnen Elementen zusammensetzt, von Google anders behandelt wird als ein CLS, der nur von wenigen Elementen erzeugt wird, ist unklar.

Änderungen in der Breite von Elementen werden ähnlich beim CLS berücksichtigt. Löst eine Breitenänderung eine Umgruppierung anderer Elemente aus, was wiederum einen Höhen-Shift auslösen kann, wird Beides im CLS berücksichtigt.

Weitere Tests für Flexbox-Größenänderungen im Zuge eines responsiven Designs haben wir nicht gemacht. Weil diese Änderungen aber durch ein CSS-Stylesheet erzeugt werden, gehen wir davon aus, das dies keinen CLS erzeugt.

Der CLS während der „Laufzeit“ einer Page

Beim Crawlen des Google-Bots werden die Metriken der Page von Google ermittelt ohne Useraktionen zu berücksichtigen.
Machen wir Lighthouse-Messungen bildet das in etwa diese Situation ab. Google nennt diese Metriken die Lab-Daten.
Google misst zusätzlich die Metriken einer Page bei realen Usern auf der ganzen Welt, die bei ihrem Chrome Browser das „statistic reporting“ entsprechend zugelassen haben.
Das nennt Google die Field-Daten einer Page und gibt diesen Daten mehr Gewicht.

Die Field-Daten weichen teilweise erheblich von den per Crawling gemessenen Lab-Daten ab.
Beim Vergleich der Lab- mit den Field-Daten unseres Shops stellte sich heraus, dass wir einen erheblichen Unterschied in den CLS Werten hatten. Die CLS Field-Daten waren erheblich größer als unsere gemessenen Daten.
Eine nähere Analyse ergab, dass das durch das Einklappen unseres Burger-Menus erzeugt wurde.
Das gab uns weitere Hinweise, wie Google den CLS in den Field-Daten ermittelt und wir entschlossen uns das ebenfalls einmal nachzumessen.

Nicht nur das initiale Verhalten der Page wird beachtet, sondern auch das Verhalten der Page nach einer Useraktion.
Der Hinweis „Google klickt nicht“ ist daher also zumindest für die Field-Daten des CLS-Wertes nicht mehr gültig.
Google gibt weitere Hinweise im Netz (WebDev-CLS) . Hier wird berichtet, dass nach einer Useraktion erst dann erneut ein CLS berechnet wird, wenn die Reaktion auf die Useraktion länger als 500ms dauert.
Das bedeutet, dass z. B. nach einem Klick auf einen Button als Reaktion durchaus Inhalte neu ein- , ausgeblendet oder eingefügt werden dürfen, ohne dass ein CLS erzeugt wird, solange diese Aktionen innerhalb von 500ms abgeschlossen sind.
Das wollten wir genau wissen; aber wie misst man einen CLS ohne Lighthouse?
Dafür bietet Google im Netz einige Snippets und eine eigene JavaScript Bibliothek (GoogleChrome-web-vitals) an. Nach Anpassung dieser Bibliothek auf unsere Bedürfnisse konnten wir uns den CLS-Wert direkt nach einer Useraktion ausgeben lassen.
Nach einem Klick auf einen Button fügten wir direkt im Viewport zwischen den Divs test und test3-fix (s. oben) einen weiteren Inhalt auf unterschiedliche Art ein:

  • Ein- und Ausblenden durch CSS-Animationen
    Ein Panel wurde aus dem Off in den Viewport per CSS-Transition geschoben. Hier bestätigte sich Googles Aussage über die 500ms. Wird die Dauer der Transition auf unter 500ms gesetzt, wird kein CLS gemessen.
    Bei Werten über 500ms bis 1s wird ein CLS nur beim Einblenden gemessen, ist der Wert größer als 1s wird auch beim Ausblenden ein CLS erzeugt. Erklären konnten wir uns den Effekt beim Ausblenden nicht.
    Sobald aber ein ease-in oder ease-out angegeben wird, wird immer ein CLS erzeugt. Hier sollte man mit den Zeitangaben in der Animation experimentieren. Einen vernachlässigbaren CLS erreichten wir erst bei Animationsdauern unter 200ms.
  • Direktes Einblenden ins DOM
    Ein direktes erzeugen eines DOM-Knotens ist so schnell, dass die Aktion keinen CLS erzeugt, egal wie umfangreich diese zusätzlich eingefügten Inhalte sind.
    Jedoch eine Verzögerung die auftritt, wenn zum Beispiel Daten zuerst vom Server geladen werden müssen oder eine umfangreiche Ermittlung der Inhalte durch ein länger laufendes Script erfolgt, erzeugt einen CLS, wenn diese größer als 500ms ist.

Fazit für die Laufzeit einer Page

Es können erhebliche CLS-Werte entstehen, wenn nach Useraktionen Inhalte ein- oder ausgeblendet werden. Dabei ist zusätzlich zu berücksichtigen, dass Google hier auf allen Devices misst.
Wenn daher bei einem Desktop-Device viel zusätzlicher Inhalt ein- und ausgeblendet wird, kann das zu erheblich höheren CLS-Werten führen, als auf einem Mobile-Device.
Hier gilt es immer das Timing nach einem User-Event zu beachten, besonders wenn man eine Single Page Applikation (SPA) hat. Wie Google genau auf SPAs reagiert können wir hier nicht sagen, das lag nicht in unserem Fokus.
Jedoch sind solche Anwendungen noch viel genauer im Hinblick auf CLS-Werte zu analysieren als Multi Page Anwendungen.
Animationen sind mit Vorsicht einzusetzen, besonders wenn man eine verzögerte Einblendung erzeugen will.

Die Ergebnisse sind für die aktuelle Situation gültig (Stand Anfang Feb. 2021). In wieweit Google sein System zur Ermittlung des CLS noch anpassen wird bleibt abzuwarten. Google selbst hat angekündigt, dass sich jederzeit noch Änderungen ergeben können.

Andreas

techblog@walbusch.de

Kontakt Blog

Wenn Sie Fragen oder Feedback zum Technologie-Blog oder zu einem unserer Beiträge haben, können Sie sich gerne per Mail an unser Technologie-Blog Team wenden!

E-Mail: techblog@walbusch.de