Intersection Observerを使ったらスクロール系の処理が簡単になった件
今更『Intersection Observer』を知ってスクロール系の処理をやってみたら目からウロコがドバドバ出てきたので使い方をまとめてみます
こっちにもintersectionobserverを使ったものがあるので参考になればと思います
この記事の目次
Intersection Observerとは
『Intersection Observer(交差オブザーバー)』は指定した範囲(root)に監視している対象の要素が入ってきたら(交差したら)その要素に対してはもちろんのこと色々と処理を実行できる仕組みです。
この方法を知るまではスクロールやリサイズを監視しスクロール量を取得し、処理したい要素の位置を取得して処理を実行するみたいにめんどくさいことを強いられていましたが、『Intersection Observer』を使うと簡潔に実行できます。
Intersection Observerの使い方
とりあえずコピペしていじれるようなひな形から
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 |
// 交差を監視する要素を準備 const targets = document.querySelectorAll('p'); // 範囲の設定 const options = { root: null, rootMargin: '-50px 0px', threshold: 0 }; // Intersection Observerを使えるようにする const observer = new IntersectionObserver(intersect, options); // 対象の要素をそれぞれ監視する targets.forEach(target => { observer.observe(target); }); // 交差したときに実行する関数 function intersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // 監視中の要素が交差した状態ならtrue // 監視中の要素が交差したときの処理 } else { // 監視中の要素が交差してない状態ならfalse // 監視中の要素が交差していないときの処理 } }); } |
細かい事はこの下にコードを抜き出しつつ説明的なことを書いています
交差を監視する要素を準備
1 2 |
// 交差を監視する要素を準備 const targets = document.querySelectorAll('p'); |
Intersection Observerで監視対象とする要素は『document.querySelectorAll()』で取得していきます
監視対象とする要素が単一の場合は「document.querySelector()」でも対応可能ですが、『document.querySelectorAll()』は監視対象となる要素が単一の場合でも対応できるのでこちらを使う方法がおすすめです
上記の雛形ではp要素を監視の対象にしています
範囲の設定
1 2 3 4 5 6 |
// 範囲の設定 const options = { root: null, rootMargin: '-50px 0px', threshold: 0 }; |
範囲の設定をしているオブジェクト『options』には3つの設定が行なえます
root
監視対象とする要素が交差しているかどうかを確認するための指定した範囲として使用される要素で、未指定の場合や、値に『null』を指定した場合はブラウザーのビューポートが指定した範囲として使用されます。
特定の要素を指定したい場合は「document.querySelector()」を利用して要素を指定します
上記の雛形ではnullを指定しているのでブラウザのビューポートが基準になっています
rootMargin
CSSのmarginプロパティのようにrootの周りにmarginを指定することができます。
マイナスの値を指定すると指定した範囲を狭めることもできます。
デフォルトでは0になっているようです
上記のひな形では’-50px 0’としていますのでビューポートに50px以上侵入してきたら処理を実行するように指定しています
threshold
thresholdははしきい値で、監視対象の要素がどのくらいの割合交差したときに処理を実行するかを指定します。
使える値は0~1までの数値で、監視対象の要素の20%が見えたら処理を実行するのであれば0.2を指定します。
20%を超える度に処理を実行したりすることもでき、その場合は、[0, 0.2, 0.4, 0.6, 0.8, 1]という配列を指定します。
rootに対して監視している要素が大きすぎるとうまく動かないケースがあるので大きな数値は使いにくい
Intersection Observerを使えるようにする
1 2 |
// Intersection Observerを使えるようにする const observer = new IntersectionObserver(intersect, options); |
いわゆる初期化・実行する際のコードで1つ目の引数には交差したときに実行するコールバック関数を、2つ目の引数には範囲の設定で作った「options」を入れています。
コールバック関数は監視対象の要素が全て入った『entries』と、コールバックが呼び出される「IntersectionObserver」を『observer』で返します
対象の要素をそれぞれ監視する
1 2 3 4 |
// 対象の要素をそれぞれ監視する targets.forEach(target => { observer.observe(target); }); |
observe()は監視対象に追加するメソッドで監視対象の要素は配列の形になっているのでforEachで一つづつtargetに取り出して引数に入れて処理しています
交差したときに実行する関数
1 2 3 4 5 6 7 8 9 10 |
// 交差したときに実行する関数 function intersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { // 監視中の要素が交差した状態ならtrue // 監視中の要素が交差したときの処理 } else { // 監視中の要素が交差してない状態ならfalse // 監視中の要素が交差していないときの処理 } }); } |
『entries』はforEachで一つづつentryに取り出してそれぞれ処理をしていきます
if文の条件にある「isIntersecting」はプロパティで監視中の要素が設定した範囲と交差したらtrueを返してくれるのでこちらで状況を判断しつつ処理を実行できます。
実際に交差した監視対象の要素を対象に処理を実行したい場合は「target」プロパティを利用することで取得できるので『entry.target』に対して処理をしてください
『observer.unobserve(entry.target);』で監視を終了することもできるので状況に合わせて試してください
実装サンプル
とりあえず触れるようにサンプルを用意してみましたのでベースになるHTMLから
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 |
<body> <header></header> <main> <section> <div> <h2>JUGEMU</h2> <figure><img src="jugemu.svg" alt=""></figure> <p>Jugemu Jugemu</p> <p>Gokō-no Surikire</p> <p>Kaijarisuigyo-no</p> <p>Suigyōmatsu Unraimatsu Fūraimatsu</p> <p>Kūnerutokoro-ni Sumutokoro</p> <p>Yaburakōji-no Burakōji</p> <p>Paipopaipo Paipo-no Shūringan</p> <p>Shūringan-no Gūrindai</p> <p>Gūrindai-no Ponpokopii-no Ponpokonā-no</p> <p>Chōkyūmei-no Chōsuke</p> </div> </section> <section> <div> <h2>寿限無</h2> <figure><img src="jugemu.svg" alt=""></figure> <p>寿限無 寿限無</p> <p>五劫の擦り切れ</p> <p>海砂利水魚の</p> <p>水行末 雲来末 風来末</p> <p>食う寝る処に住む処</p> <p>藪ら柑子の藪柑子</p> <p>パイポパイポ パイポのシューリンガン</p> <p>シューリンガンのグーリンダイ</p> <p>グーリンダイのポンポコピーのポンポコナーの</p> <p>長久命の長助</p> </div> </section> </main> <a href="#">TOP</a> <footer></footer> </body> |
邪魔かもしれないけど一応cssも
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 51 52 53 54 55 56 |
@charset 'utf8'; * { margin: 0; padding: 0; box-sizing: border-box; } header { height: 50px; background-color: #292929; } footer { height: 350px; background-color: #292929; } section { background-color: #eee; padding: 40px; } section:nth-child(even) { background-color: #ddd; } div { max-width: 600px; margin: 0 auto; text-align: center; } h2 { margin-bottom: 40px; } p { font-size: 20px; line-height: 1.6; margin-bottom: 20px; } a { display: flex; justify-content: center; align-items: center; color: #fff; text-decoration: none; width: 80px; height: 80px; background-color: #292929; position: fixed; right: 0; bottom: 50px; } |
sample1 やかましいぐらいにfadein
とりあえず手軽さアピールのためにdiv要素のすべての子要素を監視の対象として処理をするサンプルを作りました
cssではこんな感じのコードを追加しました
1 2 3 4 5 6 7 8 9 10 11 12 13 |
h2, p, img { opacity: 0; transform: translateY(50px); } h2.active, p.active, img.active { opacity: 1; transform: translateY(0); transition: opacity 1s ease-in-out, transform 1s ease-in-out; } |
Intersection Observerの設定はデフォルトで、処理は交差したときにその要素に対してクラスactiveを付与するようにしてみました。
こいつをbodyの閉じ直前に配置していきます
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 |
<script> // 交差を監視する要素を準備 const targets = document.querySelectorAll('h2,p,img'); // Intersection Observerの設定 const options = { root: null, rootMargin: '0px', threshold: 0 }; const observer = new IntersectionObserver(intersect, options); // 対象の要素をそれぞれ監視する targets.forEach(target => { observer.observe(target); }); // 交差したときに実行する関数 function intersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('active'); observer.unobserve(entry.target);//監視を終了する } }); } </script> |
sample2 jQueryを併用しつつthresholdを使う
thresholdを使ったときの動作確認とjQueryで処理したほうが楽な場合もあるかもしれないので併用するときのサンプルも用意してみました
cssはこんな感じのコードを追加しておきます
1 2 3 4 5 6 7 8 9 10 |
section { background-color: #eee; padding: 40px; opacity: 0; } section.active { opacity: 1; transition: opacity 1s ease-in-out; } |
このサンプルではsection要素を監視の対象とし、section要素の50%が交差したときに処理を実行していきます。
処理は交差した要素をjQueryオブジェクトにしつつjQueryのaddClassでクラスactiveを付与していきます
もちろんですがjQueryを読み込んでいないと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 |
<script> // 交差を監視する要素を準備 const targets = document.querySelectorAll('section'); // Intersection Observerの設定 const options = { root: null, rootMargin: '0px', threshold: 0.5 }; const observer = new IntersectionObserver(intersect, options); // 対象の要素をそれぞれ監視する targets.forEach(target => { observer.observe(target); }); // 交差したときに実行する関数 function intersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { $(entry.target).addClass('active'); observer.unobserve(entry.target);//監視を終了する } }); } </script> |
昔こんな処理で手こずった記憶があったので試してみたら簡単すぎたので一応サンプルとして紹介
今回作ったサンプルのベースではTOPボタンは下から50pxの位置に固定配置してあります。
すなわちfooterがrootMArginを上下-50pxと指定するとfooterとボタンが交差した瞬間消すことができそうです
交差した要素そのものを処理の対象にしていないケースとして参考になればと思います
なお、めんどくさいので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 |
<script> const targets = document.querySelectorAll('footer'); // Intersection Observerの設定 const options = { root: null, rootMargin: '-50px 0px', threshold: 0 }; const observer = new IntersectionObserver(intersect, options); // 対象の要素をそれぞれ監視する targets.forEach(target => { observer.observe(target); }); // 交差したときに実行する関数 function intersect(entries) { entries.forEach(entry => { if (entry.isIntersecting) { $('a').fadeOut(); } else { $('a').fadeIn(); } }); } </script> |
本当は目からウロコなんて落ちない
出るのは目やにだけ