目次
画面内にある要素の見つけ方
Webサイトの構築において、「画面外の画像は画面内に入ってから読み込む(LazyLoad)」「画面外の要素は非表示にしておき、画面内に入ったらアニメーションで表示させる」「一定の位置までスクロールしたらAjaxで無限スクロールする」といった要件は多いかと思います。
そういった処理を行う場合、必然的に「要素が画面内にある」ことを認識するトリガーが必要となります。
従来は計算で求めていた
従来は、画面のスクロール量から要素の位置関係を計算して割り出すことが一般的でした。
要件に応じて調整が入ることはありますが、おおよそは以下のようなJavaScriptですね。
1 2 3 4 5 6 7 8 9 10 11 |
$(window).on('load scroll resize', function () { $('.lazy-item').each(function () { // 画面に収まった要素をactive let targetStart = $(this).offset().top // 画面上部からのターゲットの位置 let targetEnd = targetStart + $(this).innerHeight() + $(window).innerHeight() // 画面下部からのターゲットの位置 let scloll = $(window).scrollTop() + $(window).innerHeight() / 2 // ページ最上部からのスクロール量 + ビューポートの半分 = 画面の中間 scloll = $(window).scrollTop() + $(window).innerHeight() / 2 // ページ最上部からのスクロール量 + ビューポートの半分 = 画面の中間に走査線を張る if (scloll > targetStart && scloll < targetEnd) { // ターゲットの上端~下端に走査線が入っている $(this).addClass('active') // アクティブにする } }) }) |
しかし、このスクリプトには問題点があります。スクロールするたびに対象要素の位置関係を計算しているので、非常にパフォーマンスが悪いんです。特にLazyLoadの場合はサイト閲覧の負荷低減が目的であるのに、この処理自体が足を引っ張るという矛盾がありました。
最近はブラウザから聞き出せる(Intersection Observer API)
上記に変わる代替技術として、「Intersection Observer API」が登場しました。これは、ブラウザ側が現在のViewport(表示領域)に収まっている要素を非同期に検出してくれるAPIです。
ブラウザの表示領域と交差(Intersection)した要素を検出し、要素の情報をスクリプト側で受け取る事ができます。
このAPIの登場により、わざわざスクリプト側の計算で位置関係を割り出さずとも、画面内に入った要素の情報をブラウザから直接受け取れるようになり、パフォーマンスを大幅に向上させることが可能になりました。Webサイトの快適性が問われている昨今で、これを使わない手はないですね。
しかし一点問題があります、見出しで触れたとおり、「最近」出たAPIであるという点です。少なくとも、古いブラウザは単純には使えないという認識で間違いありません。
そのため、実運用が可能なのかを明確にする必要はあります。次項ではこの観点を突き詰めていきます。
Intersection Observer APIの現状
実験的な機能
出典:Intersection Observer API – MDN web docs
Mozillaでは「実験的な機能」として紹介されています。ちゃんとした検証なしに使ってはいけないということです。
出典:Can I use ?
確かに、ブラウザの導入率には難があり、IEとSafariは全く対応していないというデータがあります。該当ブラウザではIntersection Observerをトリガーとするイベントが起きないわけなので、かなり致命的な問題になってしまいますね。
ポリフィルはある
しかし、以下のポリフィルを使えば互換性を確保できます。
1 |
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script> |
ポリフィルは、古いブラウザに存在しない機能を使用したい時、古いブラウザでは既存の機能を組み合わせて新機能の挙動を再現させるというものです。
上記のポリフィルは、IE、Safari、古いブラウザがIntersection Observer APIの挙動を求められた時、従来の計算型の方法で擬似的に再現した挙動を取ってくれるというわけです。
当然、Intersection Observer APIの導入意義である負荷の低減は見込めませんが、動かないという致命的不具合は回避されます。
また、対応しているブラウザではポリフィルがロードされず、正式なIntersection Observer APIを使うため、対応ブラウザではきちんと恩恵を得られます。
GoogleはLazyLoadの手段として推奨
Googleは、ボリュームが多いページの画像にLazyLoadをかける手法として、Intersection Observer APIの使用を薦めています。
Offscreen Images
すなわち、SEO的にも差し支えない手法ということになります。GoogleBotが認識できず、画像が正常にインデックスされないなどの心配はしなくて良さそうです。
以上の事柄から、既に実用に値するAPIと結論づけられます。次項では、早速サンプルを作ってみようかと思います。
Lozad.jsでIntersection Observer API入門(画像のLazyLoad)
Intersection Observer APIはそのまま使うと若干難易度が高いのですが、このAPIを非常に簡単に扱えるLozad.jsというライブラリがあります。
公式ページ(GitHub)
特にLazyLoadは驚くほど簡単に実現できます。早速コードを書いていきたいと思います。
HTML
HTML側では以下のように準備します。
1 2 3 4 5 6 7 |
<img class="lozad" data-src="https://unsplash.it/600/400?random&n01" src="https://placehold.it/600x400/1D417A/FFFFFF/?text=Loading..." width="55%"> <img class="lozad" data-src="https://unsplash.it/600/400?random&n02" src="https://placehold.it/600x400/1D417A/FFFFFF/?text=Loading..." width="55%"> ~中略~ <img class="lozad" data-src="https://unsplash.it/600/400?random&n38" src="https://placehold.it/600x400/1D417A/FFFFFF/?text=Loading..." width="55%"> <img class="lozad" data-src="https://unsplash.it/600/400?random&n39" src="https://placehold.it/600x400/1D417A/FFFFFF/?text=Loading..." width="55%"> <script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.4.0/lozad.min.js"></script> |
LazyLoadをかけるimgタグはクラス名に「lozad」、「data-src」にロードする画像URLが必要です。
srcに画像URLを入れておくと、ロード前のダミー画像を設置できます。こちらは省略可能です。
最後に、ポリフィルとLozad.jsをロードしています。いずれもCDNから簡単に呼び出すことができます。
JavaScript
JS側では以下のように準備します。
1 |
lozad().observe() |
おわりです。
勿論、もっと複雑な挙動が必要であったり、細かく調整したい場合はオプションを設定していきますが、単にLazyLoadをするだけでしたらこの記述だけで動きます。非常に簡単です。
サンプル
Lozad.js(Intersection Observer)でLazyLoad
画面に収まった画像から順次ロードが始まります。簡単なコードでサクサクと動いている様が見られるかと思います。
Lozad.jsに関しましては、公式ページに更に詳しい用法が記載されていますので、参考にすればより踏み込んだ実装が可能です。
スクロールに応じたアクションのニーズは演出や負荷対策の観点で高まっており、それらを低負荷で実現できるIntersection Observer APIと、実装を補助するLozad.jsは非常に有望といえます。しっかりと押さえておきたいですね。