【JavaScript】DOM要素がresizeイベントを捕捉してくれないときの対処法

更新日:2023/04/06

DOM要素のサイズ変更を捕捉したくてaddEventListener()でresizeイベントを登録したけれど反応してくれませんでした。
調べてみるとresizeイベントは、windowのリサイズ専用のようです。

どうやらDOM要素のサイズ変更は、Resize Observer APIを使用するようです。
そこで今回は、Resize Observer APIの使用方法をお伝えします。

 

ResizeObserver()の使用方法

Resize Observer APIは、DOM要素のサイズ変更を監視して、コールバック関数で報告するブラウザのAPIです。

まずは、ResizeObserverオブジェクトのインスタンスを作成します。

手順1:インスタンス作成

const resizeObserver = new ResizeObserver( コールバック関数 )

コールバック関数は、DOM要素がリサイズされたときに呼び出される関数です。
コールバック関数については、次項で解説します。

次に、取得したインスタンスのobserveメソッドで、要素のリサイズ監視を開始します。

手順2:監視開始

resizeObserver.observe( DOM要素 , observedBoxオプション );

observedBoxオプションは省略できます。

複数の要素を監視したいときは、要素ごとにobserveメソッドを実行しましょう。

監視を停止するときは、unobserveメソッドまたはdisconnectメソッドを使用します。

備考:監視終了

resizeObserver.unobserve( DOM要素 );
 // または
resizeObserver.disconnect();

unobserve()は、要素単位で監視を停止します。
disconnect()は、全ての要素の監視を停止します。

監視の再開は、もう一度observe()を呼び出します。

 

ResizeObserverのコールバック関数

ResizeObserverのインスタンス生成時に、コールバック関数を引数で渡します。

このコールバック関数は、ResizeObserverEntryの配列を受け取ります。

次のコードは、ResizeObserverでサイズ変更を監視する例です。

コールバック関数の例

const div1 = document.getElementById( "div1" );
const div2 = document.getElementById( "div2" );

const resizeObserver = new ResizeObserver( 
    entries => {
        entries.forEach( entry =>{
            const {target,contentRect} = entry;
            const name = target === div1 ? "div1" : "div2";
                    
            console.log( `${name}の幅が${contentRect.width} 高さが${contentRect.height}に変更されました`);
                
        });
});
resizeObserver.observe( div1 );
resizeObserver.observe( div2 );

監視対象が一つのときはループさせる必要はありませんが、受け取った引数が配列なので[0]を参照します。

const resizeObserver = new ResizeObserver( 
    entries => {
        const {target,contentRect} = entry[0];
        });
});

 

ResizeObserverEntryのプロパティ

コールバック関数に渡されるResizeObserverEntryのプロパティを表にしました。

プロパティは5つあります。
それらはオブジェクトのため、プロパティを持っています。

プロパティ名意味プロパティのプロパティ
プロパティ名意味
borderBoxSize要素のサイズblockSize高さ
inlineSize
contentBoxSizeコンテンツのサイズblockSize高さ
inlineSize
devicePixelContentBoxSizeコンテンツのサイズ
(デバイスピクセル単位)
blockSize高さ
inlineSize
contentRectコンテンツのサイズ
※非推奨
x外枠からのx座標
y外枠からのy座標
height高さ
width
top上辺のy座標
bottom下辺のy座標
left左辺のx座標
right右辺のy座標
target対象の要素各要素のプロパティ

要素のサイズは、線幅・パディングを含めたサイズです。
コンテンツのサイズは、線幅・パディングを除いたサイズです。

デバイスピクセル単位は画面上のサイズです。
例えば、ブラウザのズーム機能で100%以外の値を指定しているとき、contentBoxSizeとdevicePixelContentBoxSizeの値が異なります。

contentRectは様々な情報が取得できて使い勝手がいいのですが、初期段階のもので現在は互換のために残されています。
そのため、今後の流れによっては使用できなくなる可能性があります。
できる限り使用しない方がよさそうです。

 

observedBoxオプションについて

observeメソッドの第二引数は、サイズ変更監視をボーダーボックスまたはコンテンツボックスのどちらで行うのかを指定できます。

observeメソッド

resizeObserver.observe( DOM要素 , observedBoxオプション );

上記の構文のobservedBoxオプションですね。
これは、次オブジェクトを指定します。

{ box : 文字列 }

文字列は、次の3つを使用できます。

"border-box"(規定値)、"content-box""device-pixel-content-box"

実際のところ、連動して変化することが多いのであまり意味がない気がします。

また、連動しないような状況を設定しても通知されたりされなかったりと不明な点が多いです。

例えば、次のようにタイマーでパディングと線幅を変更します。

setTimeout(()=>{
    console.log( "padding変更" );
    div.style.padding = "5px";
},1000);
    
setTimeout(()=>{
    console.log( "borderWidth" );
    div.style.borderWidth = "5px";
},2000);

divは、次のようなタグです。

<div id="div1" style="width:300px;height: 50px;border:1px solid gray;"></div>

この場合、タイマーによりボーダーボックスのサイズが変更されますが、コンテンツボックスは変更されません。

そしてオプション値を設定して実行した、結果は次の通りです。

"border-box"→通知なし。
"content-box"→通知なし。

両方とも、通知されませんでした。

つぎに、divのスタイルに"box-sizing: border-box;"を追加します。

<div id="div1" style="box-sizing: border-box;width:300px;height: 50px;border:1px solid gray;"></div>

この場合、タイマーによりボーダーボックスのサイズが変更なしで、コンテンツボックスが変更されます。

そして結果は、次のようになりました。

"border-box"→通知あり。
"content-box"→通知あり。

両方とも通知されました。

この様子を見ると、コンテンツボックスのサイズ変更を監視している気がしますね。

どちらにしろ、observedBoxオプションがお仕事をしていない印象です。
よくわかりません。

これが仕様通りかどうかはResize Observerの仕様を読めばいいのですが、そこまでやる意味がないのでやっていません。

なお今回はFirefox111.0.1とChrome111.0.5563.147で確認しています

更新日:2023/04/06

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

スポンサーリンク

記事の内容について

null

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

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

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

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

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

 

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