DOM

【JavaScript】DOM要素が画面上に表示されたときイベントを発生させる方法

更新日:2023/05/19

JavaScriptでDOM要素がブラウザのビュー内に入ったときに、動きを持たせて読者に興味を持たせたい。
そんなニーズが意外と多いです。

そこで今回は、DOM要素が読者の目に入ったときにコールバックで通知を行う方法をお伝えします。

 

Intersection Observer API

DOM要素がブラウザのビュー内に表示されたときに通知を受け取るにはIntersection Observer APIを使用します。

Intersectionは交差、Observerは観察者という意味があるので、DOM要素が特定の領域と交差するのを監視するAPIという意味です。

 

Intersection Observer APIの使い方

Intersection Observer APIは、次のように使用します。

  // 交差オブザーバーの生成
const observer = new IntersectionObserver(
    (entries, observer) => entries.forEach(
        entry => {
            const entity = entry.target;  // 対象のDOM要素を取得

            if( entry.isIntersecting ){

                  // ビューに入った時の処理

            }else{ // 

                  // ビューから外れた時の処理

            }
        }
     ));
  // 交差オブザーバーの開始
observer.observe( DOM要素 );

まずは、IntersectionObserverコンストラクターに引数としてコールバック関数を渡して、インスタンスを生成します。
次にインスタンスのobserveメソッドを呼び出して、引数で渡したDOM要素の監視を開始します。

 

マージンを指定する

ビューに余白を持たせて、その内側にDOM要素が入ったら通知を得ることもできます。

内側にDOM要素が入ったら通知

次のように、IntersectionObserverコンストラクターの第二引数を指定します。

const observer = new IntersectionObserver( コールバック関数 , {
                   rootMargin:"-20% 0px -20% 0px",
                });

rootMargin:"-20% 0px -20% 0px"は、上下20%の範囲を余白に設定しています。
画面の内側に余白を設定するときは、マイナス指定します。
プラス指定すると画面外に設定されます。

 

要素の表示割合を指定する

ビュー内にDOM要素がどれくらい入ったら通知するのかも指定できます。

要素の表示割合を指定する

次のように、IntersectionObserverコンストラクターの第二引数を指定します。

const observer = new IntersectionObserver( コールバック関数 , {
                   threshold:0.5,
                });

threshold:0.5は、DOM要素の50%のラインがビューの内側に入ったときに通知されます。
0なら0%つまり少しでも表示されたときです。
1なら100%つまり要素全てが表示されたときです。

この値は配列での指定もできます。
[0 , 0.5 , 1]なら、0%50%100%で通知されます。

 

監視を終了する

要素の監視を終了するときは、IntersectionObserverコンストラクターのインスタンスからunobserveメソッドを呼び出します。
引数はDOM要素です。

const observer = new IntersectionObserver( コールバック関数 );
observer.observe( DOM要素1 );
observer.observe( DOM要素2 );

observer.unobserve( DOM要素1 ); // DOM要素1の監視を停止

全ての監視を停止するときは、disconnect()を呼び出します。
引数はありません。

const observer = new IntersectionObserver( コールバック関数 );
observer.observe( DOM要素1 );
observer.observe( DOM要素2 );

observer.disconnect( ); // DOM要素1とDOM要素2の監視を停止

 

具体例:フェードイン

Intersection Observer APIの具体例として、DOM要素がビュー内に入ったら少しずつ表示(フェードイン)されるコードを挙げてみます。

次のようなhtmlを想定しています。

<div class="scroll_view_area" style="border:1px solid gray;box-sizing: border-box;">
        <div>
            <p>こんにちは!</p>
        </div>
    </div>

JavaScriptは次のようになります。

Array.from( document.getElementsByClassName("scroll_view_area") ).forEach(
        e=>{
 
            const inner_div = e.getElementsByTagName("div")[0];

            (new IntersectionObserver(
                (entries, observer)=>{
                    entries.forEach(entry => {
                        [inner_div.style.transition ,inner_div.style.opacity]
                            = entry.isIntersecting ? ["all 5s","1"] : ["none","0"];
 
                    });
                    
                },{rootMargin:"-20% 0px -20% 0px",threshold:1})).observe( e );
            
        }
    );

要素がエリア内に入ると、次のスタイル属性がセットされます。

transition: all 5s
opacity: 1

これにより、5秒かけて完全な透明(表示されていない状態)から完全な不透明(表示されている状態)に切り替わっていきます。

要素がエリアから出ると、次のスタイル属性がセットされます。

transition: none
opacity: 0

これにより即座に、完全な透明(表示されていない状態)に切り替わります。

■上記のコードのデモです。

こんにちは!

 

具体例:cssでの状態切り替え

cssの@keyframesなどを使ってDOM要素にアニメーションを行わせたいときなどは、次のようなコードでクラスを変更します。

Array.from( document.getElementsByClassName("scroll_view_area") ).forEach(
        e=>{
            const inner_div = e.getElementsByTagName("div")[0];
            inner_div.classList.add( "scroll_view_area_init");

            (new IntersectionObserver(
                (entries, observer)=>{
                    entries.forEach(entry => {
                        inner_div
                            .classList[ entry.isIntersecting ? "add" : "remove" ]("scroll_view_area_on");
                    });
                },{rootMargin:"-20% 0px -20% 0px",threshold:1})).observe( e );
        }
    );

スタイルは、次のようになります。

.scroll_view_area_init{
    height:3em;
    position: relative;
}
.scroll_view_area_init p{
    position: absolute;
    display: none;
}
.scroll_view_area_init.scroll_view_area_on p{
    display: block;
    animation: 3s both move_p;
}
@keyframes move_p { from { top: -180px;left:100px; background-color: #e74c3c; } to { top: 0;left:0; background-color: #ffffff;}  }

■コードのデモ

こんにちは!

 

具体例:1回のみ通知を受けとる

通知を1回だけ受けとり、以降は監視を停止するときは、通知を受け取った後にunobserve()を呼び出します。

Array.from( document.getElementsByClassName("scroll_view_area") ).forEach(
        e=>{
 
            const inner_div = e.getElementsByTagName("div")[0];
            [inner_div.style.transition ,inner_div.style.opacity]=["none","0"];

            (new IntersectionObserver(
                (entries, observer)=>{
                    entries.forEach(entry => {
                        if( entry.isIntersecting ){
                            [inner_div.style.transition ,inner_div.style.opacity]=["all 5s","1"];
                            observer.unobserve( entry.target );
                        } 
                    });
                    
                })).observe( e );
            
        }
    );

上のコードはエリアから外れたときも通知を受け取るので、entry.isIntersectingで状態を確認しています。
また、初期状態としてコードで要素を非表示にしています。

cssで最初から非表示にしていないのは、ブラウザ設定でJavaScriptを停止している可能性を考慮しているからです。

更新日:2023/05/19

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。