🐢

HTMLのLazy Loadの挙動がブラウザによってかなり異なるので違いを実測して確かめてみた

2023/11/22に公開
3

htmlのLazyLoadは画像やiframeなどの要素が必要となる時までデータのロードを遅らせる技術ですが、実際の挙動は一般に思われているような 「ユーザーの目(viewPort)に入った時」 という認識とは異なっており、また各ブラウザでも「必要」の解釈がかなりの差があったので計測用のページを作って各ブラウザごとの差異を実測してみました。

<img loading="lazy" width="800" height="461" src="/images/mamorioTag.webp">

計測方法と結果

以下のようになlazy loadが発火した時点でのソースと画面中央の距離(px)を測るページを用いて、iframeと画像の二種類のソースを対象に4000pxのdiv要素とボタン以外に何も読み込まれていない状態とすでに画像が読み込まれている状態との二つの状態を掛け合わせて計測しました。

import React, { useState, useRef } from 'react';

export default function Home() {
  const iframeDistance = 4000;
  const [showImage, setShowImage] = useState(false);
  const imageRef = useRef(null); 
  const toggleImage = () => {
    setShowImage(!showImage);
  };

  const calculateDistanceToViewportCenter = () => {
    const imageRect = imageRef.current.getBoundingClientRect();
    const viewportCenter = window.innerHeight / 2;
    const distanceToViewportCenter = imageRect.top + imageRect.height / 2 - viewportCenter;
    return distanceToViewportCenter;
  };

  return (
    <div>
      <div style={{ background: 'gray', height: `${iframeDistance}px` }} >
        <button onClick={toggleImage}>putImage</button>
        {showImage && <img src="images/testcat.jpg" alt="testcat" />}
      </div>
      <img
        ref={imageRef} // Attach the ref to the image
        src="images/testcat2.webp"
        alt="Business Image"
        loading='lazy'
        onLoad={() => {
          const distanceToCenter = calculateDistanceToViewportCenter();
          alert(`lazyload fired!: Current scroll position: ${window.scrollY}px, distance to viewport center: ${distanceToCenter}px, Browser: ${navigator.appName}, Version: ${navigator.appVersion}`);
        }}
      />
    </div>
  );
}

画像の読み込みのテストを行うページ

https://iototaku.github.io/lazytestimage/

iframeの読み込みのテストを行うページ

https://iototaku.github.io/lazytestiframe/

結果は以下の通りでした。

2025年1月25日追記

Safari: ユーザーの目に入る直前までiframe読み込みを遅延する

MacとiOS双方のSafariで特徴的なのはiframeの場合は要素のかなり近くまでスクロールしないと読み込まれません。iOSのテーブルビューのようにユーザーの目に触れていないものは極力遅延させメモリの使用を控える設計思想があるのかもしれません。

ChromeとEdge: 遅延よりも先読みを優先

Chromeは以下の発表にある通り回線速度やすでに読み込んだデータ量を踏まえてどの程度遅延させるかをフレキシブルに変化させる戦略のようですが、実測をしたところではViewPortよりかなり下(2000px以上)の領域まで先読みが行われlazyを指定してもロードの対象になります。また画像よりもiframeの方がより早く先読みの対象になり、Chromeと同じくChroniumを用いているEdgeでも同様の挙動となります。

https://web.dev/articles/browser-level-image-lazy-loading?hl=ja#improved-data-savings-and-distance-from-viewport-thresholds

もともと私がこの調査をはじめたきっかけはいくら画面下部のiframeにlazy loadを指定してもPCのChromeでページが読み込まれた瞬間にロードされてしまう問題にぶち当たったことだったのですが、ユーザーの目に触れない領域のロードを送らせてトップページの表示速度を優先する場合はlazyではなくIntersectionObserverを用いるなど別の戦略を取る必要がありそうです。
Appleの思想とは異なり遅延よりも先読みを優先する戦略が伺えます。

AndroidのChrome: すでに画像がロードされている時は遅延しない

AndroidのChromeはLazy Loadが指定されている要素よりも先に画像がロードされた場合は画像とiframeのいずれも遅延ロードが一切適用されません。ユーザーの目に触れるまでロードをさせないつもりでlazyを指定しても効果がない可能性が高いので実機でのテストが必要です。

Firefox: ChromeとSafariの中間くらいの遅延(2024年1月25日追記)

本記事執筆時点(2023年11月22日)のFirefoxでは実装されていなかったiframeの遅延読み込みが12月にリリースされた新バージョン121では実装され、Safariと同じようにユーザーの目にまでiframeのロードを遅延させる仕様になっていました。

遅延ロードが発火するまでの距離がChrome(2683)とSafari(555)の中間になっているところに他社への意識が伺えます。

# Firefox: iframeには遅延しない

Firefoxで計測を行なって意外だったのはiframeの遅延ロードが一切効かないことです。画像にはChromeに比べてやや長め(1600px)の遅延が適用されます。

まとめ

HTMLのLazyLoadは画像などのサイズの大きいリソースの読み込みを遅延させることでフロントエンドの表示速度を向上させる有効な手段ではありますが、何をもってあるリソースにロードする必要が発生したと捉えるかの解釈についてのブラウザ毎の違いが大きく現れるため動作にかなりのばらつきがありますので、想定するユーザーが使うブラウザでの実際の挙動の確認が必要です。

Discussion

myakuramyakura

こんにちは。

Firefoxで計測を行なって意外だったのはiframeの遅延ロードが一切効かないことです。

とありますが、iframeでのloading属性は12月リリースのFirefox 121でサポートされるので、試した時点で動いてないかと思われます。
https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/121#html

iototakuiototaku

ご指摘ありがとうございます。121がリリースされたら確かめて更新します

iototakuiototaku

2023年12月にリリースされたFirefox ver121でiframeの遅延ロードが実装されていたので追記しました。