
スクロールイベントって、実装が複雑だったり、パフォーマンスが気になったりしませんか?
そんなスクロールイベントを置き換えられるかもしれないAPIが、IntersectionObserverです。
IntersectionObserverを使えば、
よく見る“ふわっと表示”をはじめ、
要素の表示トリガーやメニューの切り替えなどを、
スクロールイベントなしでシンプルに実装できます。

上は“ふわっと表示”のデモGIF。これをスクロールイベント無しで作れます。
この記事【基礎編】では、“ふわっと表示”をはじめとした、
スクロールイベントでよく実装されるアニメーションを題材に、
IntersectionObserverの基本的な使い方を紹介します。
目次
“ふわっと表示”を作ってみる
さて、ここからIntersectionObserverの説明に入っていきます。
…が、その前に。
「とりあえず動くコードが欲しい」
「細かい説明はあとでいい」
「さっさとコピペしたい」
という方もいるでしょう。
ということで、まずは いちばんシンプルな“ふわっと表示”を作ってみましょう。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | // オプション設定 // 条件: // ターゲット要素の3割が画面に入ったら // 補足: // rootMarginで交差範囲を縮めすぎてしまうと、 // ページ最下部でスクロールできる余白が足りずに // 監視対象が一生交差しないままになってしまうことがあります。 const option = { root: null, rootMargin: "0px", threshold: 0.3, } // インスタンス生成 // コールバック関数とオプションを渡します const observer = new IntersectionObserver(doWhenIntersect, option); // 監視対象にしたい要素を渡す document.querySelectorAll('.js--fade-item').forEach((el) => { observer.observe(el); }); // コールバック関数 function doWhenIntersect(entries) { entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける entry.target.classList.add('is-intersecting'); // 一度表示された要素は、再び監視する必要がないため、 // クラス付与後に unobserve で監視対象から外します observer.unobserve(entry.target); } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | // オプション設定 // 条件: // ターゲット要素の3割が画面に入ったら // 補足: // rootMarginをマイナスにしすぎると、 // ページ最下部でスクロールできる余白が足りずに // 監視対象が一生交差しないままになってしまうことがあります。 const option = { root: null, rootMargin: "0px", threshold: 0.3, } // インスタンス生成 // コールバック関数とオプションを渡します const observer = new IntersectionObserver(doWhenIntersect, option); // 監視対象にしたい要素を渡す $('.js--fade-item').each(function() { observer.observe(this); }) // コールバック関数 function doWhenIntersect(entries) { entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける $(entry.target).addClass('is-intersecting'); // 一度表示された要素は、再び監視する必要がないため、 // クラス付与後に unobserve で監視対象から外します observer.unobserve(entry.target); } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!-- ふわっとさせたい要素にクラス名を付与(今回はjs--fade-item) --> <section class="section-each js--fade-item" id="section-1"> <div class="inner-block"> <div class="contents-wrap"> <h2 class="ttl js--fade-item">セクション1</h2> <div class="img-area js--fade-item"><img src="https://picsum.photos/id/25/620/320.webp" loading="eager" alt=""></div> <p class="txt js--fade-item">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint dolores consequatur minima. Consequatur magni aliquid tempore suscipit hic distinctio tenetur.</p> </div> </div> </section> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | /* ここはお好みで */ .js--fade-item { opacity: 0; translate: 0 20px; -webkit-transition: opacity 0.6s ease-out; transition: opacity 0.6s ease-out; } .js--fade-item.is-intersecting { opacity: 1; -webkit-animation: scroll-blur 0.4s 0.2s forwards; animation: scroll-blur 0.4s 0.2s forwards; } @-webkit-keyframes scroll-blur { 100% { translate: 0 0; } } @keyframes scroll-blur { 100% { translate: 0 0; } } |
これだけです。
ここで何をしているか、超ざっくりと説明。
- 要素が画面に入ったら
- クラスを付けて
- CSSでふわっと表示する
たったこれだけで、“ふわっと表示”を
IntersectionObserverが軽く・ズレなくやってくれます。
IntersectionObserverの基礎知識
IntersectionObserverを使うときに知っておきたいことを、ざっくりと見ておきましょう。
IntersectionObserverとは
IntersectionObserver(インターセクションオブザーバー) は、
ある要素が画面(または特定の領域)にどれくらい見えているか を
ブラウザが自動で判定してくれる API です。
スクロールイベントのように
「今どこまでスクロールされたか?」を毎回自分で計算する必要がなく、
要素が見えた/見えなくなったなどのタイミングだけを効率よく通知してくれます。
たとえば、
要素が画面に入ったら
要素が画面の◯%見えたら
要素が画面から外れたら
といった判定をしたい場合、
scrollイベントよりも IntersectionObserver の方が
軽く、正確に実装できます。
「IntersectionObserverって難しそう…?」と思うかもしれませんが、
基本の使い方は驚くほどシンプル。
観測したい要素と、どれくらい見えたら反応するかをオプションで設定し、要素が入ったら実行したい処理を設定するだけです。
scrollイベントとの比較
要素が画面に入ったら、というような処理を作るときによくつかわれるscrollイベント。
しかし、scrollイベントはスクロールをするたびに発火するため、処理が重くカクカクになってしまったりするのがネックになることがよくあるかと思います。
scrollイベントだと・・・
イベントが頻繁に発火する
スクロールすると “1px動いただけでも” scroll イベントが走る
自前で可視判定ロジックを書く必要がある
デバイス・環境によっては重くなりがち
IntersectionObserver を使うと・・・
「要素がビューポート(または指定の root)と交差したか/見えはじめたか/見えなくなったか」 をブラウザが効率よくチェックして通知してくれます。
必要な時だけ発火
可視判定ロジックはオプション設定するだけ
複数要素の監視も簡単
こんなときに置き換えをおすすめ
要素が 「見えた/見えなくなった」で処理」 する
(フェードイン、表示トリガー、メニュー切り替えなど)
監視対象が 複数 ある
(リスト、ギャラリー、記事一覧など)
スクロールが 重く感じる、スマホでカクつく
要素の可視状態だけ知りたい
!これはスクロールイベント(量)が必要
こんな時には、IntersectionObserverではなくスクロールイベントの出番です。
スクロール量に応じて連続的に動かす表現(パララックスなど)は、
IntersectionObserverではなく scrollイベントの役割になります。
スクロール量に応じて連続的に動かしたい
スクロールに合わせて少しずつ移動・フェードさせる
スクロール進捗と完全に同期したUIを作りたい
進捗バー、数値カウントなど
スクロール速度や勢いを使った演出をしたい
勢いで動く、慣性がある表現など
パフォーマンスの差
スクロールイベントとのパフォーマンスの差を詳しく知りたい方はこちらの記事を参照ください。(英語記事)
https://itnext.io/1v1-scroll-listener-vs-intersection-observers-469a26ab9eb6
対応デバイス・バージョン等
ChromeやEdge、Firefox等の主要ブラウザ、iOS SafariはiOS 12.2以降対応しています。
一般的なWebサイト制作であれば、安心して使えますね。
↓対応状況の最新情報はこちらから↓
https://caniuse.com/?search=IntersectionObserver
ここで気を付けてほしいのが、IntersectionObserverV2の存在です。
V2 では、
・要素が「どれくらい前面にあるか」を示す isVisible
・より精度の高い可視判定
など、便利そうな機能が追加されています。
しかし、V2は主要ブラウザではChromeとEdgeしか対応しておらず、iOS Safariは対応皆無です。
↓V2の対応状況の最新情報はこちらから↓
https://caniuse.com/?search=IntersectionObserver+V2
V2 専用のオプションやフィールドの使用には注意してください。
V2について、詳しくはこちらの記事を参考にしてください。
https://web.dev/articles/intersectionobserver-v2?hl=ja
仕組みを理解する
IntersectionObserverの基本の書き方・使い方を説明していきます。
デモページを見ながら仕組みを理解していきましょう。

基本のコード
まずは、今回のデモで使っている基本のコードを見てみましょう。
この時点では、細かい意味が分からなくても大丈夫です。
以下の順でコードを分解しながら、1つずつ説明していきます。
①オプションの設定
②インスタンス生成
③監視対象を渡す
④コールバック関数を定義する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // オプション設定 const option = { root: document.querySelector('.intersection_observer .intersection-root'), rootMargin: "20px 0% -50% 0%", threshold: 0.3, } // インスタンス生成 // コールバック関数とオプションを渡します const observer = new IntersectionObserver(doWhenIntersect, option); // 監視対象にしたい要素を渡す document.querySelectorAll('.target-block').forEach((el) => { observer.observe(el); }); // コールバック関数 function doWhenIntersect(entries) { entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける entry.target.classList.add('is-intersecting'); } else { // クラスを外す entry.target.classList.remove('is-intersecting'); } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // オプション設定 const option = { root: document.querySelector('.intersection_observer .intersection-root'), rootMargin: "20px 0% -50% 0%", threshold: 0.1, } // インスタンス生成 // コールバック関数とオプションを渡します const observer = new IntersectionObserver(doWhenIntersect, option); // 監視対象にしたい要素を渡す $('.target-block').each(function() { observer.observe(this); }) // コールバック関数 function doWhenIntersect(entries) { entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける $(entry.target).addClass('is-intersecting'); } else { // クラスを外す $(entry.target).removeClass('is-intersecting'); } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <main class="intersection_observer demo-base"> <div class="intersection-root"> <!-- rootに指定したコンテナ --> <div class="target-container"> <div class="target-block"> <!-- 監視対象 --> TARGET </div> <div class="target-block"> <!-- 監視対象 --> TARGET </div> <div class="target-block"> <!-- 監視対象 --> TARGET </div> </div> </div> </main> |
1 2 3 4 5 6 7 | /* ここはお好みで */ .target-block.is-intersecting { background-color: crimson; } |
① オプションの設定
IntersectionObserverは、ここで設定するオプションを基準に動作し処理を行います。
IntersectionObserver の基本のオプション(root / rootMargin / threshold)を、
「なにをどう設定すればいいの?」が分かるように整理します。
1 2 3 4 5 | const option = { root: document.querySelector('.intersection_observer .intersection-root'), rootMargin: "20px 0% -50% 0%", threshold: 0.3, } |
- root:基準にする領域(通常は画面 = null)
root は「何を基準に交差判定するか(ビューポート or 特定の要素)」です。
本記事では、root と rootMargin をあわせた判定エリアを「交差範囲」と呼びます。
基本は root: null(=画面/ビューポート基準)でOK。
ただし、スクロールできる要素(例:overflow:auto の枠)の中で判定したい場合は、その要素を root に指定します。 - rootMargin:判定エリアを広げる/狭める
rootMargin は、交差範囲を広げたり狭めたりできます。
プラス値にすると交差範囲を広げ、マイナス値にすると交差範囲を狭めます。
書き方は CSS の margin や padding と同じで、 “top right bottom left” の順です。
値はpxまたは%で指定ができ、pxまたは%を付けることが必須です。デモページでは赤色の範囲がプラスの値、グレーの範囲がマイナスの値です。
- threshold:どれくらい見えたら反応するか(割合)
threshold は「ターゲットがどれくらい見えたら反応するか(割合)」です。
0.3 なら “3割見えたら反応”。
配列(例:[0,0.25,0.5,0.75,1])にすると、監視対象が各割合で交差範囲をまたぐたびに、という条件指定ができるようになります。(この仕様を利用した例は応用編で公開予定です)
!rootMarginとthresholdの使い分け方
迷いやすいrootMarginとthresholdの使い分け。
このように覚えておきましょう。
「監視対象が何割見えたら(3割見えたらなど)」を条件にしたい => threshold
「画面の範囲またはこの位置に来たら(中央・上から100pxなど)」を条件にしたい => rootMargin
どちらも併用できますが、まずはどちらか一方で考えるとシンプルです。
② IntersectionObserverの生成
次に、IntersectionObserver のインスタンスを作成します。
1 | const observer = new IntersectionObserver(doWhenIntersect, option); |
第1引数:コールバック関数
要素が交差したときに実行される関数を渡します。
第2引数:オプション
root / rootMargin / threshold などのオプション設定を渡します。
③ 監視対象を渡す
次に、監視してほしい要素を先ほど生成したIntersectionObserverに渡します。
1 2 3 | document.querySelectorAll('.target-block').forEach((el) => { observer.observe(el); }); |
監視してほしい要素を、.observe()でIntersectionObserverに渡します。
④ コールバック関数の中身
最後に、要素が交差したときに実行される処理を書きます。
②で渡したコールバック関数の中身になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | function doWhenIntersect(entries) { // コンソールでentriesの中身を見てみる console.log(entries); entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける entry.target.classList.add('is-intersecting'); } else { // クラスを外す entry.target.classList.remove('is-intersecting'); } }); } |
まず、コールバック関数には配列が渡されるので、それをentriesとして受け取ります。
複数の監視対象が同時に交差することもあるので、「交差状態が変わった要素」を配列にまとめて渡してくれます。
コールバック関数の中でconsole.log(entries)してみると、中身はこんな感じになっています。

entries(配列)の中身は、[IntersectionObserverEntry]。
0: IntersectionObserverEntryのように、各監視対象の状態がまとまって入っています。
各監視対象には、状態を示す項目がずらりと入っていますが、ひとまず本記事で使用しているこの2つを覚えておきましょう。
-
target
交差した監視対象 -
isIntersecting
交差しているかどうか(true = 交差範囲に入っている / false = 交差範囲に入っていない)
以下3つは、応用としてささっと目を通しておきましょう。
-
intersectionRatio
交差範囲にどれくらい見えているか(0〜1の割合) -
boundingClientRect
ターゲット要素の位置・サイズ(getBoundingClientRect()相当) -
intersectionRect
実際に交差している部分の矩形(“見えている部分”の領域)
基本的にはtargetとisIntersectingの使い方さえ押さえれば十分です。

上のGIFでは、②のインスタンス生成の後のconsole.log(observer);、
コールバック関数の中でconsole.log(entries);、
そしてスクロールし、監視対象が交差した時のconsole.log(entries);の中身を見ています。ここでは交差した監視対象のみがentriesの中に入っています。
オプションで設定した条件のもとで監視対象が交差したときに、再度コールバック関数が呼び出されてコンソールに表示されています。
つまり 「条件を満たしたタイミングだけ通知される」のがIntersectionObserverのポイントです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | entries.forEach(entry => { // 要素が交差したら… if (entry.isIntersecting) { // クラスをつける entry.target.classList.add('is-intersecting'); } else { // クラスを外す entry.target.classList.remove('is-intersecting'); } }); |
さきほどおさえた基本の2つを使用しているのがこの部分になります。
① forEachでentries(交差した監視対象が入った配列)の中の各監視対象をentryとして使う。
② entryが交差範囲に交差しているかどうかをif文で見る
entry.isIntersectingがtrueの場合:
entry.targetで、監視対象のDOM要素にis-intersectingというクラスを付与。
entry.isIntersectingがfalseの場合:
entry.targetで、監視対象のDOM要素にis-intersectingというクラスを削除。
これでIntersectionObserverの基本の書き方・使い方をコンプリートしました。
ここまで理解できれば、“ふわっと表示”や表示切り替え系の実装は
ほぼすべて対応できるようになります。
よくある使用例
ここからは、IntersectionObserverで作りやすい定番アニメーションを紹介します。
基本の形は同じで、変わるのは次のポイントだけです。
- 監視対象(どの要素を見るか)
- 判定条件(rootMargin / threshold)
- コールバック関数内の
isIntersectingの分岐
ターゲットが見えなくなったら…

ここでやっていること
- 監視対象:
MVセクション - 判定条件:
MVセクションが画面内に10%以上見えているか - MVが画面内にある間:
ヘッダーは通常表示 - MVがほぼ画面外になったら:
ヘッダーにmv-outクラスを付ける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | // オプション設定 // 条件: // ターゲット要素の1割が画面に入ったら // 補足: // ターゲット要素が1pxでも画面に入ったら、にしたい場合は // rootMarginもthresholdもゼロでOK const option = { root: null, rootMargin: "0px", threshold: 0.1, } const observer = new IntersectionObserver(doWhenIntersect, option); document.querySelectorAll('.js--io-mv').forEach((el) => { observer.observe(el); }); function doWhenIntersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // MVが画面の内 → クラスを消す document.querySelector('.io-header').classList.remove('mv-out'); } else { // MVが画面の外 → クラスを付ける document.querySelector('.io-header').classList.add('mv-out'); } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | (function ($) { 'use strict'; // オプション設定 // 条件: // ターゲット要素の1割が画面に入ったら // 補足: // ターゲット要素が1pxでも画面に入ったら、にしたい場合は // rootMarginもthresholdもゼロでOK const option = { root: null, rootMargin: "0px", threshold: 0.1, } const observer = new IntersectionObserver(doWhenIntersect, option); $('.js--io-mv').each(function() { observer.observe(this); }); function doWhenIntersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // MVが画面の内 → クラスを消す $('.io-header').removeClass('mv-out'); } else { // MVが画面の外 → クラスを付ける $('.io-header').addClass('mv-out'); } }); } })(jQuery); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | <header class="io-header"> <div class="inner-block"> <div class="logo-area"> <span class="txt">IntersectionObserverのデモページ</span> </div> <ul class="header-list"> <li><a href="#section-1">セクション1</a></li> <li><a href="#section-2">セクション2</a></li> <li><a href="#section-3">セクション3</a></li> </ul> </div><!-- /inner-block --> </header> <!-- ターゲットにクラス名を付与(今回はjs--io-mv) --> <section class="mv-section section-each js--io-mv"> <div class="inner-block"> <div class="contents-wrap"> <h1 class="ttl">MVが見えなくなったら...</h1> <p class="txt">IntersectionObserverを使って、<br>MVが画面外になったらヘッダーにクラスをつけます。</p> </div> </div> <div class="img-area"><img src="https://picsum.photos/id/29/1920/600?grayscale&blur=2" alt=""></div> </section> |
1 2 3 4 5 6 7 8 9 10 11 12 | /* ここはお好みで */ .io-header.mv-out { background: rgba(248, 248, 255, 0.9); color: DarkSlateGrey; border-color: DarkSlateGrey; } .io-header.mv-out .header-list { color: DarkSlateGrey; } |
ヘッダーナビと連動

ここでやっていること
- 監視対象:
各メインセクション、MVセクション - 判定条件:
対象が画面中央に入ったか - MVが画面内にある間:
ヘッダーリストは通常表示 - 各メインセクションが中央ラインに来たら:
各メインセクションと対応するヘッダーリストにis-currentクラスを付ける
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | // オプション設定 // 条件: // ターゲット要素が中央ラインに来たら // 補足: // 上と下のマージンが-50%、つまり画面のちょうど真ん中が交差範囲になります const option = { root: null, rootMargin: "-50% 0px", threshold: 0, } const observer = new IntersectionObserver(doWhenIntersect, option); // 今回は各セクションと、ヘッダーリストにはないMVを監視してもらう document.querySelectorAll('.js--io-section, .mv-section').forEach((el) => { observer.observe(el); }); function doWhenIntersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { if (entry.target.classList.contains('mv-section')) { // 要素が交差している、かつその要素がMVである場合 document.querySelectorAll('.io-header .header-list li').forEach(el => { // ヘッダーリストのis-currentを外す el.classList.remove('is-current'); }); } else { // 各セクションのidを取得し、ヘッダーリストと対応するものをtargetとする const id = entry.target.getAttribute('id'); const target = document.querySelector('.io-header .header-list li a[href="#' + id + '"]'); // 一旦すべてのヘッダーリストのis-currentを外す document.querySelectorAll('.io-header .header-list li').forEach(el => { el.classList.remove('is-current'); }); // 対象のヘッダーリストにクラスをつける if (target) { target.parentElement.classList.add('is-current'); } } } }); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | (function ($) { 'use strict'; // オプション設定 // 条件: // ターゲット要素が中央ラインに来たら // 補足: // 上と下のマージンが-50%、つまり画面のちょうど真ん中が交差範囲になります const option = { root: null, rootMargin: "-50% 0px", threshold: 0, } const observer = new IntersectionObserver(doWhenIntersect, option); // 今回は各セクションと、ヘッダーリストにはないMVを監視してもらう $('.js--io-section, .mv-section').each(function() { observer.observe(this); }); function doWhenIntersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { if ($(entry.target).hasClass('mv-section')) { // 要素が交差している、かつその要素がMVである場合 // ヘッダーリストのis-currentを外す $('.io-header .header-list').find('li').removeClass('is-current'); } else { // 各セクションのidを取得し、ヘッダーリストと対応するものをtargetとする const id = entry.target.getAttribute('id'); const target = $('.io-header .header-list').find('li a[href="#' + id + '"]').parent('li'); // 一旦すべてのヘッダーリストのis-currentを外す $('.io-header .header-list').find('li').removeClass('is-current'); // 対象のヘッダーリストにクラスをつける target.addClass('is-current'); } } }); } })(jQuery); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | <header class="io-header"> <div class="inner-block"> <div class="logo-area"> <span class="txt">IntersectionObserverのデモページ</span> </div> <ul class="header-list"> <li><a href="#section-1">セクション1</a></li> <li><a href="#section-2">セクション2</a></li> <li><a href="#section-3">セクション3</a></li> </ul> </div><!-- /inner-block --> </header> <!-- ターゲットにクラス名を付与(今回はjs--io-section) --> <section id="section-1" class="js--io-section section-each"> <div class="inner-block"> <div class="contents-wrap"> <h2 class="ttl">セクション1</h2> <div class="img-area"><img src="https://picsum.photos/id/25/620/320.webp" loading="eager" alt=""></div> <p class="txt">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint dolores consequatur minima. Consequatur magni aliquid tempore suscipit hic distinctio tenetur.</p> </div> </div> </section> <section id="section-2" class="js--io-section section-each"> <div class="inner-block"> <div class="contents-wrap"> <h2 class="ttl">セクション2</h2> <div class="img-area"><img src="https://picsum.photos/id/28/620/320.webp" loading="eager" alt=""></div> <p class="txt">Lorem ipsum dolor sit amet consectetur, adipisicing elit. Sint dolores consequatur minima. Consequatur magni aliquid tempore suscipit hic distinctio tenetur.</p> </div> </div> </section> <section id="section-3" class="js--io-section section-each"> <div class="inner-block"> <div class="contents-wrap"> <h2 class="ttl">セクション3</h2> <div class="img-area"><img src="https://picsum.photos/id/33/620/320.webp" loading="eager" alt=""></div> <p class="txt">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores fuga, excepturi ratione aliquid, molestiae at ex quos voluptate consectetur quod alias, debitis veniam nam! Itaque magni deserunt, illum, veniam rerum ut porro doloribus repellat cumque odit corporis minus ad amet!</p> </div> <div class="contents-wrap"> <h2 class="ttl">セクション4</h2> <div class="img-area"><img src="https://picsum.photos/id/38/620/320.webp" loading="eager" alt=""></div> <p class="txt">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Asperiores fuga, excepturi ratione aliquid, molestiae at ex quos voluptate consectetur quod alias, debitis veniam nam! Itaque magni deserunt, illum, veniam rerum ut porro doloribus repellat cumque odit corporis minus ad amet!</p> </div> </div> </section> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* ここはお好みで */ .header-list > li a { display: inline-block; padding-block: 4px; position: relative; } .header-list > li a::after { position: absolute; content: ""; width: 30px; height: 2px; background: Gold; bottom: 0; left: 0; right: 0; margin: auto; opacity: 0; -webkit-transition: opacity 0.3s ease-in-out; transition: opacity 0.3s ease-in-out; } .header-list > li.is-current a::after { opacity: 1; } |
まとめ
IntersectionObserver、使ってみると意外と楽で、
「これ、もっと早く知りたかったやつだ…」となった方もいるかもしれません。
今回紹介した基礎的なアニメーション以外にも、
横スクロールやスライダーと組み合わせると、
ちょっと面白い表現も作れます。
そういった内容は、応用編でまとめる予定です。
また、Observer 系には ResizeObserver など、
UIの変化を検知できる便利な API もあります。
「スクロールイベントやリサイズイベントで頑張る前に、
Observerでできないか?」を考えてみると、
実装がぐっと楽になるかもしれません。



