Natives Lazy Loading mit 1x1 Platzhalter und Kumulative Layoutverschiebung (CLS)

  • So, mal einen neuen Tread dazu. Vielleicht sieht da ja einer was vor lauter Bäumen, ich leider nicht mehr.


    An sich erst mal alles gut. Eingebunden ist das IMG im HTML wie folgt:


    <img class="b-lazy" loading="lazy" src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" data-src="/bilder/geranien-aus-keller-k.jpg" width="300" height="225" />


    Die Klasse "b-lazy" macht hier eigentlich nichts, als das Bild erst mal auf hidden zu setzen. Die Größenangaben im HTML direkt passen. Das 1x1 Pixel wird also gestaucht, aber egal, sieht man ja nicht. Der effektive Raum, der freigelassen wird, stimmt.


    So, nun das Problem im zeitlichen Ablauf.


    1. Anfangs stimmt alles


    2. CSS wird geladen. Passt auch noch alles, denn keine Klasse macht irgendwas mit Größen, nur Sichtbarkeit


    3. Javascript wird gelesen. Erkennt dass natives Lazy-Loading verfügbar ist und ändert den Quelltext. Das Bild aus "data-src" wird in "src" eingefügt und die zusätzliche Klasse "b-loaded" gesetzt. Diese beinhaltet nur die Angaben "height: auto;" und setzt das ganze eben wieder auf "visible". Nun kommt es aber zum Fehler, denn "auto" veranlasst nun den Platzhalter, auf eine Größe von 300x300 zu gehen, das im HTML-Code angegebene 225 wird dann ignoriert. Das Bild shifted, der Platzhalter wird zu groß.


    4. Das dauert dann 10-50ms, sehr unterschiedlich, bis der Browser dann das echte Bild lädt. Wenn das dann da ist, dann passt die Höhe wieder, denn das echte Bild hat eben die height="225".


    So, hat nun einer eine Ahnung, wie ich das Problem aus Punkt 3 umgehen kann?


    Ich könnte das "height: auto;" einfach weglassen, dann ist das Problem auch weg, aber ein neues kommt dann. Dann sind die Bilder nämlich nicht mehr responsive :(


    Es gibt leider auch keine Möglichkeit zu erkennen, wann das Bild nativ geladen ist. Sonst könnte man die Klasse "b-loaded" ja erst später einfügen. Wie aber gesagt, es gibt beim nativen LazyLoading keine Rückmeldung. Wohin auch, das geht ja alles ganz ohne Script :(


    Den Platzhalter-Pixel auf einen anderen Formfaktor zu bringen ist auch keine wirkliche Option, denn die eigentlichen Bilder sind alles komplett unterschiedliche Formate und ich weiß vorher nicht, was kommt oder ich brauche, bzw. wären das viel zu viele unterschiedliche. Zudem würde es ein Pixel mit 1x0,66 oder 1,33x1 ja gar nicht geben ;)

    Don't judge a book by its cover @ Jadyn Rylee
    "Sogar ein Mann mit reinstem Gemüt, der Gebete sagt jede Nacht, kann zum Wolf werden, wenn die Wolfsblume blüht unter des Mondes goldener Pracht"

  • Erkennt dass natives Lazy-Loading verfügbar ist und ändert den Quelltext. Das Bild aus "data-src" wird in "src" eingefügt und die zusätzliche Klasse "b-loaded" gesetzt. Diese beinhaltet nur die Angaben "height: auto;" und setzt das ganze eben wieder auf "visible". Nun kommt es aber zum Fehler, denn "auto" veranlasst nun den Platzhalter, auf eine Größe von 300x300 zu gehen, das im HTML-Code angegebene 225 wird dann ignoriert. Das Bild shifted, der Platzhalter wird zu groß.


    Mach mal den Versuch und nimm ein von dir gehostetes Bild, schön komprimiert und direkt verlinkt.

    Google oder Lighthouse hat mal wieder an pagespeed herumgespielt und lazyloading ist nun im Nachteil.

    Frischen Browser kann ich dir empfehlen für den Test.

  • Ähm, ja, nein, doch, keine Ahnung was.


    Ja, Lighthouse und Co haben umgestellt. Die Field-Daten werden neu erfasst. Genau darum geht es.


    Wenn das Bild schön komprimiert ist.... Was meinst Du damit? Hoch komprimiert sind die bei mir alle, aber eben so, dass sie noch vernünftig sind. Ich könnte natürlich als Platzhalter ein JPG nehmen, das 100% Kompression hat. Das würde aber nicht helfen, denn wesentlich größer als 1x1 und ein extra Request pro Bild. Das 1x1 ist dann ja im Cache, auch wenn es 100 mal geladen werden muss.


    Das Problem aktuell ist ja die Browser-Unterstützung. Nutzt man nur "natives Lazy Load", also das 'loading="lazy"', dann ist das Problem umgangen und eigentlich alles rein aus HTML, so wie vor 10 Jahren. Aber das können eben nicht alle Browser, also Polyfill mit "Intersection Observer" oder eben einer ganzen eigenen JS-Klasse (in meinem Fall eine optimierte B-Lazy). Ich hier habe also drei. Nativ, Intersection oder Klasse.


    Ich kann hier also kein normales Bild als "src" angeben, denn das wird nicht von allen unterstützt. So weit ich weiß, auch wenn der Google-Bot nun Ever-Green ist, kann er es auch nicht. Der würde also von Lazy in dem Fall nichts mitbekommen, wenn das Bild direkt im "src" steht.


    P.S: Meine Browser sind alle auf dem aktuellen Stand. Die Suma-Bots aber nicht ;)


    Lazyloading im Nachteil: Ja, genau.... Vor 3 Monaten hieß es noch, man soll es machen. Nun habe ich das endlich überall drinnen, dann kommt Google mit CLS, das alles über den Haufen wirft. Also Chromium, was ja zu großen Teilen Google ist, sagt ja, nutze es, Google und sein Bot oder Lighthouse sagt nun nein.

    Don't judge a book by its cover @ Jadyn Rylee
    "Sogar ein Mann mit reinstem Gemüt, der Gebete sagt jede Nacht, kann zum Wolf werden, wenn die Wolfsblume blüht unter des Mondes goldener Pracht"

  • Hat dein lazyload irgendwelche events? Wenn du style="display:none;" am bild setzt, dann ist das wichtiger als das was class="..." sagt, und du kannst es in lazyload mit $img.removeAttr('style') wieder anzeigen wenn das bild dann wirklich fertig geladen ist.

  • Danke für die Nachfrage. Auch hier, ja und nein. Events sind da, aber nicht bei allen drei Optionen. Wie gesagt, drei Wege in genau der Reihenfolge


    1. Natives LazyLoad per 'loading="lazy"'

    2. Fallback zu Intersection Observer (mit Events)

    3. Fallback zur eigenen Klasse (mit Events)


    Bei den Optionen 2 und 3 tritt das Problem nicht auf. Das Bild steht quasi im "data-src", wird per JS geladen und dann ins normale src verschoben, sobald der Onload da ist. Da spielt das height:auto also keine Rolle, denn wenn die Klasse gesetzt wird, dann ist das Bild schon da.


    Das gibt es bei Option 1 aber nicht. Diese Option funktioniert im Grunde völlig ohne JS, wenn man keinen Fallback braucht oder will. Da steht das Bild dann ganz normal im src.


    Mit Fallback aber nicht möglich, denn dann würde der Browser ohne native Unterstützung alles sofort laden. Hier entsteht aber das Problem, denn nur wegen dem Fallback muss auch bei "Nativ" das Bild ins data-src und dann eben per mini JS, wenn "nativ" erkannt wird, direkt ins src kopiert werden. Genau da passiert es. Der Platzhalter geht dann auf Grund seiner 1x1 auf 300x300 und nicht auf z.B. 300x200.


    Natürlich kann ich per "style" arbeiten, aber das endet auch in der Luft, denn es gibt keine Rückmeldung vom nativen LazyLoad. Man weiß also nicht, wann man das Style dann wieder entfernen oder durch height:auto ersetzen kann. Und height:auto muss her, wegen responsive.


    Irgendwie beißt sich die Katze da in den Schwanz und dreht sich im Kreis dabei.


    Dann gibt es ja so eine schöne CSS-Option: "aspect-ratio" auch in Verbindung mit "width" und "height". Damit könnte man die Attributwerte auslesen und eben dem System / Element dynamisch mitteilen, es soll gefälligst die vorgegebenen Angaben einhalten. Geht aber auch nicht, denn aspect-ratio funktioniert nur mit Blockelementen wie DIVs und so, nicht mit Bildern.


    Dann schon einen ganz anderen Weg versucht, wobei der unnötigen Ballast erzeugt. Dem umliegenden <figure> einfach die Bildgröße zu geben und ein overflow:hidden. Schön gedacht, aber falsch, denn im figure ist auch ein figcaption und dessen Höhe ist unbekannt. Das Ergebnis ist, das 1x1-Pixel bleibt falsch groß, wird aber richtig angezeigt, nun passt das Element <figure> an sich aber nicht mehr, weil die figcaption nun "hidden" ist ;)

    Don't judge a book by its cover @ Jadyn Rylee
    "Sogar ein Mann mit reinstem Gemüt, der Gebete sagt jede Nacht, kann zum Wolf werden, wenn die Wolfsblume blüht unter des Mondes goldener Pracht"

  • Ich glaube, ich habe eine Lösung gefunden. Sicher bin ich mir aber noch nicht. -> SVG . Nicht als externes Bild, das geht auch nicht, aber als Data-Uri, denn da kann man die "viewBox" angeben und die hält die Vorgaben tatsächlich ein. Kein Zucken mehr :)


    src="data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20300%20225%22%3E%3C/svg%3E"


    Und der kumulative Layout Shift ist von 0,2 auf 0,015 gefallen

    Don't judge a book by its cover @ Jadyn Rylee
    "Sogar ein Mann mit reinstem Gemüt, der Gebete sagt jede Nacht, kann zum Wolf werden, wenn die Wolfsblume blüht unter des Mondes goldener Pracht"