動画の遅延読み込み

公開日: 2019 年 8 月 16 日、最終更新日: 2026 年 7 月 2 日

画像要素と同様に、動画の遅延読み込みも必要になる場合があります。動画は通常 <video> 要素で読み込まれますが、YouTube などの他のサービスでホストされている動画の場合は <iframe>s が使用されることがあります(その場合は、iframe の遅延読み込みに関する記事をご覧ください)。

<video> の遅延読み込みの方法は、ユースケースによって異なります。いくつかの異なる解決策があります。

loading 属性を使用する

ブラウザは、メディア要素(<video><audio>)の loading 属性のサポートを開始しました。サポートされている場合、loading="lazy" 属性を追加するだけで、メディアとポスター画像の両方が自動的に遅延読み込みされます。

<video controls loading="lazy" preload="none" poster="one-does-not-simply-placeholder.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

loading="lazy" を使用すると、poster 画像はビューポート内またはその近くになるまで読み込まれず、preload 属性を使用して読み込む video の量を決定します。つまり、autoplay 動画はビューポート内またはその近くになるまで自動再生されません。loading="lazy" 属性は posterpreloadautoplay 属性と連携して動作し、これらの属性の必要性を置き換えるものではありません。

メディア要素の loading 属性をサポートしていないブラウザでは、この属性は無視されます。その結果、poster 画像はすぐに読み込まれ、動画は preload 属性に基づいて読み込まれます。これについては後述します。

preload 属性を使用して、自動再生されない動画を遅延読み込みする

動画の自動再生を避けることは、通常、ユーザーに制御を任せるためにおすすめの方法です。このような場合は、preload 属性<video> 要素に指定することが、必要になるまで動画全体を読み込まないようにする最善の方法です。

<video controls loading="lazy" preload="none" poster="one-does-not-simply-placeholder.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

前の例では、preload 属性に none の値を使用して、ビューポート内にある場合でも、ブラウザが動画データをプリロードしないようにしています。 poster 属性は、動画の読み込み中にスペースを占有するプレースホルダを <video> 要素に提供します。

ほとんどのブラウザでは、preload はデフォルトで metadata に設定され、動画の一部が Content-Range ヘッダーを使用してプリロードされます。これにより、必要以上のデータがダウンロードされる可能性があります。ブラウザはメタデータが保存されているバイト数を把握できず、ファイルの先頭に保存されていない可能性があるためです。したがって、動画の読み込みを回避する最善の方法は、preload="none" を使用することです。

これをさらに強化して、ユーザーが onmouseenter 属性(または同等の mouseenter イベント ハンドラ)で動画にカーソルを合わせたときにメタデータをプリロードできます。

<video controls
  loading="lazy"
  preload="none"
  poster="one-does-not-simply-placeholder.jpg"
  onmouseenter="event.target.setAttribute('preload','metadata')">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

これにより、ユーザーが動画を再生する際の遅延が短縮されるだけでなく、カーソルを合わせたときに動画の長さも表示されます。

動画は LCP 候補となる可能性があります。poster 画像は動画よりも早く読み込まれるため、LCP 候補の場合はポスター画像を使用する必要がありますが、プリロードする必要があります。fetchpriority 属性値を "high" にしてプリロードする必要があります。現時点では、ポスター属性の fetchpriority を指定できないためです:

<link rel="preload" href="one-does-not-simply-placeholder.jpg" as="image" fetchpriority="high">
<video controls preload="none"
  poster="one-does-not-simply-placeholder.jpg"
  onmouseenter="event.target.setAttribute('preload','metadata')">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

LCP 要素である動画では、loading="lazy" を使用しないでください。これは、LCP 画像や動画を遅延読み込みしないのと同じです。

アニメーション GIF の代替として機能する動画の場合

自動再生動画は、GIF スタイルのクイック アニメーションによく使用されます。アニメーション GIF は広く使用されていますが、ファイルサイズなど、さまざまな点で動画と同等ではありません。アニメーション GIF は、数メガバイトのデータに及ぶことがあります。同様の画質の動画は、はるかに小さくなる傾向があります。

<img> 要素ほど簡単ではありませんが、<video> 要素をアニメーション GIF の代替として使用できます。アニメーション GIF には次の 3 つの特徴があります。

  1. 読み込まれると自動的に再生されます。
  2. 連続してループします(常にそうとは限りません)。
  3. 音声トラックはありません。

<video> 要素でこれを実現すると、次のようになります。

<video autoplay muted loop playsinline>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

loading="lazy" 属性を使用すると、ビューポートの近くになるまで動画が読み込まれません。autoplaymutedloop 属性は自明です。iOS で自動再生を行うには playsinline が必要です

これで、プラットフォーム間で動作する、GIF の代替となる実用的な動画ができました。では、どのように遅延読み込みすればよいでしょうか。

これをサポートするブラウザの場合、loading="lazy" が最も簡単な方法です。

<video loading="lazy" autoplay muted loop playsinline>
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

他のブラウザで動画の遅延読み込みをサポートするには、JavaScript を使用して、<video> マークアップを適宜変更する必要があります。

<video class="lazy" preload="none" controls muted loop playsinline width="610" height="254" poster="one-does-not-simply.jpg">
  <source src="one-does-not-simply.webm" type="video/webm">
  <source src="one-does-not-simply.mp4" type="video/mp4">
</video>

autoplay が削除され、class="lazy"preload="none"poster 属性が追加されています。これにより、動画が遅延読み込みされるまで <video> 要素のスペースを占有するプレースホルダを指定できます。次に、JavaScript Intersection Observer を使用して、自動再生を再度有効にします。

document.addEventListener("DOMContentLoaded", function() {
  var lazyVideos = [].slice.call(document.querySelectorAll("video.lazy"));

  if ("IntersectionObserver" in window) {
    var lazyVideoObserver = new IntersectionObserver(function(entries, observer) {
      entries.forEach(function(video) {
        if (video.isIntersecting) {
          video.target.removeAttribute('preload');
          video.target.removeAttribute('controls');
          video.target.autoplay = true;
          video.target.classList.remove("lazy");
          lazyVideoObserver.unobserve(video.target);
        }
      });
    });

    lazyVideos.forEach(function(lazyVideo) {
      lazyVideoObserver.observe(lazyVideo);
    });
  }
});

この方法では、動画はデフォルトでは自動再生されず、Intersection Observer が動画がビューポートに入ったことを検出した場合にのみ自動再生されます。また、controls 属性は JavaScript が失敗した場合に対応します。つまり、アニメーション GIF の動作をエミュレートする動画ソリューションが実現します。アニメーション GIF と同じように大量のデータを使用することなく、そのコンテンツを遅延読み込みできます。

遅延読み込みライブラリ

次のライブラリは、動画の遅延読み込みに役立ちます。

  • vanilla-lazyloadlozad.js は、Intersection Observer のみを使用する非常に軽量なオプションです。そのため、パフォーマンスは高いですが、古いブラウザで使用するにはポリフィルが必要です。
  • React 固有の遅延読み込みライブラリが必要な場合は、react-lazyload を検討してください。Intersection Observer は使用しませんが、React でアプリケーションを開発しているユーザーにとって使い慣れた画像遅延読み込みの方法を提供します。

これらの遅延読み込みライブラリはそれぞれ、さまざまな遅延読み込みの取り組みに対応するマークアップ パターンが豊富に用意されています。

より多くのブラウザで <video>loading 属性が利用可能になるにつれて、JavaScript ソリューション(ライブラリを含む)は、組み込みソリューションよりも使用されなくなることが予想されます。ただし、それよりも詳細な制御が必要な場合は除きます。