1. はじめに
このセクションは規範的ではありません。
レスポンシブWebコンポーネントは、Elementのサイズ変更に応答する必要があります。例として、地図を表示するElementがあります。
-
地図を表示する際、内容ボックスを
Elementタイルでタイル状にします。 -
サイズが変更された場合、再度タイル処理が必要になります。
レスポンシブWebアプリケーションは、ビューポートのサイズ変更にすでに対応できます。
これはCSSメディアクエリやwindow.resizeイベントで実現します。
ResizeObserver APIは、Elementのサイズ変更を監視するためのインターフェイスです。これはwindow.resizeイベントのElement版です。
ResizeObserverの通知は、Elementのサイズ変更に対応するために利用できます。これらの観測に関する興味深い事実:
-
監視対象のElementがDOMに挿入・削除されたとき、観測が発火します。
-
監視対象のElementのdisplayがnoneに設定されたとき、観測が発火します。
-
非置換インラインElementには観測は発火しません。
-
CSSのtransformによる変更では観測は発火しません。
-
監視開始時、Elementが描画中でサイズが0,0でない場合、観測が発火します。
< canvas id = "elipse" style = "display:block" ></ canvas > < div id = "menu" style = "display:block;width:100px" > < img src = "hamburger.jpg" style = "width:24px;height:24px" > < p class = "title" > メニュータイトル</ p > </ div >
// リサイズ時、elipseはキャンバス内に楕円を描画する document. querySelector( '#elipse' ). handleResize= entry=> { entry. target. width= entry. borderBoxSize[ 0 ]. inlineSize; entry. target. height= entry. borderBoxSize[ 0 ]. blockSize; let rx= Math. floor( entry. target. width/ 2 ); let ry= Math. floor( entry. target. height/ 2 ); let ctx= entry. target. getContext( '2d' ); ctx. beginPath(); ctx. ellipse( rx, ry, rx, ry, 0 , 0 , 2 * Math. PI); ctx. stroke(); } // リサイズ時、幅によってタイトルの表示・非表示を切り替える document. querySelector( '#menu' ). handleResize= entry=> { let title= entry. target. querySelector( ".title" ) if ( entry. borderBoxSize[ 0 ]. inlineSize< 40 ) title. style. display= "none" ; else title. style. display= "inline-block" ; } var ro= new ResizeObserver( entries=> { for ( let entryof entries) { let cs= window. getComputedStyle( entry. target); console. log( '監視中の要素:' , entry. target); console. log( entry. contentRect. top, ' は ' , cs. paddingTop); console. log( entry. contentRect. left, ' は ' , cs. paddingLeft); console. log( entry. borderBoxSize[ 0 ]. inlineSize, ' は ' , cs. width); console. log( entry. borderBoxSize[ 0 ]. blockSize, ' は ' , cs. height); if ( entry. target. handleResize) entry. target. handleResize( entry); } }); ro. observe( document. querySelector( '#elipse' )); ro. observe( document. querySelector( '#menu' ));
2. Resize Observer API
2.1. ResizeObserverインターフェイス
ResizeObserverインターフェイスは、Elementのサイズ変更を監視するために使われます。
これはMutationObserverやIntersectionObserverを参考に設計されています。
enum {ResizeObserverBoxOptions ,"border-box" ,"content-box" };"device-pixel-content-box"
ResizeObserverは、さまざまなCSSサイズを監視できます:
-
border-box:CSS2で定義されるボーダー領域のサイズ。 -
content-box:CSS2で定義される内容領域のサイズ。 -
device-pixel-content-box:CSS2で定義される内容領域のサイズをデバイスピクセル単位で表したものです。要素や祖先へのCSS変換適用前の値です。 このサイズは整数値でなければなりません。
device-pixel-content-box
は、devicePixelRatioをcontent-box
のサイズに掛けて概算できます。
ただし、ブラウザ固有のサブピクセルスナップ挙動のため、
著者はこのスケールされたcontent-box
サイズをどのように丸めるべきか判別できません。
UAが要素のデバイスピクセルボックスを算出する方法は実装依存です。
一例として、ボックスサイズと位置にデバイスピクセル比を掛け、その結果の浮動小数点のサイズと位置を整数値に丸め、描画品質を最大化する方法が考えられます。
このサイズはターゲットの位置変更の影響を受けるため、他のサイズより監視コストが高くなる傾向があります。
dictionary {ResizeObserverOptions ResizeObserverBoxOptions = "content-box"; };box
このセクションは規範的ではありません。著者は複数のCSSボックスを監視したい場合があります。 この場合、複数のResizeObserverを使う必要があります。
// content-boxを監視 ro. observe( document. querySelector( '#menu' ), { box: 'content-box' }); // border-boxのみを監視。前回の監視は置き換えられる。 ro1. observe( document. querySelector( '#menu' ), { box: 'border-box' });
これはイベント発火時に定義されたコールバックに返されるボックス寸法の種類には影響しません。著者がレイアウト変更を監視したいボックス種別を定義するだけです。
[Exposed =(Window ),Constructor (ResizeObserverCallback )]callback interface {ResizeObserver void observe (Element ,target optional ResizeObserverOptions );options void unobserve (Element );target void disconnect (); };
new ResizeObserver(callback)-
-
thisを新しい
ResizeObserverオブジェクトとする。 -
this.callback内部スロットにcallbackをセットする。
-
this.observationTargets内部スロットに空リストをセットする。
-
Document.resizeObserversスロットにthisを追加する。
-
observe(target, options)-
targetを監視対象要素リストに追加します。
-
targetが
observationTargetsスロットに存在する場合、unobserve(target)を呼び出す。 -
resizeObservationを新しい
ResizeObservation(target, options)とする。 -
resizeObservationをobservationTargetsスロットに追加する。
-
unobserve(target)-
targetを監視対象要素リストから削除します。
-
observationを
ResizeObservationで、observationTargets内のtargetスロットがtargetなものとする。 -
observationが見つからなければ戻る。
-
observationTargetsからobservationを削除する。
-
disconnect()-
-
observationTargetsリストをクリアする。 -
activeTargetsリストをクリアする。
-
2.2. ResizeObserverCallback
callback =ResizeObserverCallback void (sequence <ResizeObserverEntry >,entries ResizeObserver );observer
このコールバックはResizeObserverの通知を配信します。アクティブな観測の通知アルゴリズムによって呼び出されます。
2.3. ResizeObserverEntry
[Exposed =Window ]interface {ResizeObserverEntry readonly attribute Element target ;readonly attribute DOMRectReadOnly contentRect ;readonly attribute sequence <ResizeObserverSize >borderBoxSize ;readonly attribute sequence <ResizeObserverSize >contentBoxSize ;readonly attribute sequence <ResizeObserverSize >devicePixelContentBoxSize ; };
contentRectはResizeObserverのインキュベーション段階からのものであり、現行のウェブ互換性のためだけに含まれています。将来のレベルでは非推奨になる可能性があります。
target, 型 Element, 読み取り専用-
サイズが変更された
Elementです。 contentRect, 型 DOMRectReadOnly, 読み取り専用-
Elementのcontent rectであり、ResizeObserverCallbackが呼び出された時点の値です。 borderBoxSize, 型 sequence<ResizeObserverSize>, 読み取り専用-
シーケンスであり、
Elementの border boxサイズがResizeObserverCallback呼び出し時に格納されます。 contentBoxSize, 型 sequence<ResizeObserverSize>, 読み取り専用-
シーケンスであり、
Elementの content rectサイズがResizeObserverCallback呼び出し時に格納されます。 devicePixelContentBoxSize, 型 sequence<ResizeObserverSize>, 読み取り専用-
シーケンスであり、
Elementの content rectサイズ(デバイスピクセル単位の整数値)がResizeObserverCallback呼び出し時に格納されます。
ボックスサイズプロパティは、複数のフラグメントを持つ要素(マルチカラムなど)への対応のためシーケンスとして公開されています。
しかし、現行仕様で定義されるcontent rectや
border boxは、マルチカラムレイアウト時の挙動を記載していません。
本仕様ではシーケンスの中に1つだけResizeObserverSizeが返され、それは最初のカラムの寸法に対応します。
将来のバージョンでは、フラグメントごとのサイズ情報をシーケンスとして返す予定です。
interface {ResizeObserverSize readonly attribute unrestricted double ;inlineSize readonly attribute unrestricted double ; };blockSize
3. 処理モデル
3.1. ResizeObservation例構造体
このセクションは規範的ではありません。ResizeObservationはResize Observerの実装例構造体です。
本仕様の処理モデルの理解を助けるために記載されています。実質的に1つのElementの観測情報を保持します。
このインターフェイスはJavascriptからは参照できません。
[(Constructor Element ) ]target interface {ResizeObservation readonly attribute Element target ;readonly attribute ResizeObserverBoxOptions observedBox ;readonly attribute sequence <ResizeObserverSize >lastReportedSizes ; };
target, 型 Element, 読み取り専用-
観測対象の
Elementです。 observedBox, 型 ResizeObserverBoxOptions, 読み取り専用-
どのボックスが監視されているか。
lastReportedSizes, 型 sequence<ResizeObserverSize>, 読み取り専用-
最後に報告されたサイズの順序付きシーケンス。
new ResizeObservation(target, observedBox)-
-
thisを新しい
ResizeObservationオブジェクトとする -
this内部の
targetスロットにtargetをセットする -
this内部の
observedBoxスロットにobservedBoxをセットする -
this内部の
lastReportedSizesスロットに[(0,0)]をセットする
-
isActive()-
-
currentSizeをtargetとobservedBoxを使ってボックスサイズを計算で設定する。
-
currentSizeがthis.
lastReportedSizesの最初のエントリと異なる場合、trueを返す。 -
falseを返す。
-
3.2. 内部スロット定義
3.2.1. Document
Documentは、resizeObserversスロットを持ちます。これはこのドキュメント内のResizeObserverのリストです。初期値は空です。
3.2.2. ResizeObserver
ResizeObserver
は、コンストラクターで初期化されるcallbackスロットを持ちます。
ResizeObserver
は、observationTargetsスロットを持ちます。これはResizeObservationのリストであり、監視中の全てのElementを表します。
ResizeObserver
は、activeTargetsスロットを持ちます。これはResizeObservationのリストであり、直前の観測通知以降にサイズが変更され、通知対象となるElementを表します。
ResizeObserver
は、skippedTargetsスロットを持ちます。これはResizeObservationのリストであり、直前の観測通知以降にサイズが変更されたが通知対象とならないElementを表します。
3.3. CSS定義
3.3.1. content rect
DOM content rectは次の値を持つrectです:-
widthはcontent widthです。
-
heightはcontent heightです。
-
topはpadding topです。
-
leftはpadding leftです。
content width仕様はマルチカラムレイアウトが内容ボックスにどう影響するか記載していません。本仕様では、Elementがマルチカラム内にある場合のcontent
widthはgetComputedStyle(element).widthの結果です。現状では最初のカラムの幅になります。
content rectの位置をpadding-top/leftにすることで、ターゲットの子要素の絶対配置が便利になります。絶対座標空間の原点はパディング矩形の左上です。
content rectを監視する場合:
-
監視対象ElementがDOMに挿入・削除された時、観測が発火します。
-
監視対象Elementのdisplayがnoneに設定された時、観測が発火します。
-
非置換インラインElementは常に空のcontent rectを持ちます。
-
CSS transformによる変更では観測は発火しません。
ウェブコンテンツにはSVG要素も含まれます。SVG要素はcontent boxの代わりにbounding boxを定義します。 SVGGraphicsElementのcontent rectは次の値を持つrectです:
-
widthはbounding boxの幅です。
-
heightはbounding boxの高さです。
-
topおよびleftは0です。
3.4. アルゴリズム
3.4.1. 指定された深さでアクティブな観測を収集
これはdocumentの全てのアクティブな観測を計算します。指定した深さでアクティブな観測を収集するには、次の手順を実行します:
-
depthを指定された深さとする。
-
resizeObservers内の各observerについて、次の手順を実行:-
observerの
activeTargetsとskippedTargetsをクリアする。 -
observer.
observationTargets内の各observationについて、次の手順を実行:-
observation.
isActive()がtrueの場合-
targetDepthがdepthより大きければ、
activeTargetsにobservationを追加する。 -
それ以外の場合は
skippedTargetsにobservationを追加する。
-
-
3.4.2. アクティブな観測があるか
Documentにアクティブな観測があるかを判定するには、次の手順を実行します:
-
resizeObservers内の各observerについて、次の手順を実行:-
observer.
activeTargetsが空でなければtrueを返す。
-
-
falseを返す。
3.4.3. スキップされた観測があるか
Documentにスキップされた観測があるかを判定するには、次の手順を実行します:
-
resizeObservers内の各observerについて、次の手順を実行:-
observer.
skippedTargetsが空でなければtrueを返す。
-
-
falseを返す。
3.4.4. ResizeObserverEntryの作成と設定
ResizeObserverEntryの作成と設定を、与えられたtargetに対して行うには、次の手順を実行します:-
thisを新しい
ResizeObserverEntryとする。 -
thisの
targetスロットにtargetを設定する。 -
thisの
borderBoxSizeスロットに、targetと"border-box"のobservedBoxでサイズを計算した結果を設定する。 -
thisの
contentBoxSizeスロットに、targetと"content-box"のobservedBoxでサイズを計算した結果を設定する。 -
thisの
devicePixelContentBoxSizeスロットに、targetと"device-pixel-content-box"のobservedBoxでサイズを計算した結果を設定する。 -
thisの
contentRectに、targetと"content-box"のobservedBoxでthis.contentBoxSizeを設定する。 -
targetがSVG要素でない場合、次の手順を実行:
-
this.contentRect.topにtargetのpadding topを設定する。
-
this.contentRect.leftにtargetのpadding leftを設定する。
-
-
targetがSVG要素の場合、次の手順を実行:
-
this.contentRect.topおよびthis.contentRect.leftに0を設定する。
-
3.4.5. アクティブな観測の通知
アクティブな観測の通知は、ドキュメント内のすべてのアクティブな観測を配信し、通知対象の最も浅い深さを返します。
ドキュメントのアクティブな観測を通知するには、次の手順を実行します:
-
shallowestTargetDepthを∞とする。
-
document.
resizeObservers内の各observerについて、次の手順を実行:-
observerの
activeTargetsスロットが空の場合は続ける。 -
entriesを空の
ResizeObserverEntryリストとする。 -
activeTargets内の各observationについて、次の手順を実行:-
entryを、observation.
targetを与えてResizeObserverEntryの作成と設定を実行した結果とする。 -
entryをentriesに追加する。
-
observationの
lastReportedSizesに一致するentryのサイズを設定する。-
一致するサイズは、observation.
observedBoxが"border-box"ならentry.borderBoxSize -
一致するサイズは、observation.
observedBoxが"content-box"ならentry.contentBoxSize -
一致するサイズは、observation.
observedBoxが"device-pixel-content-box"ならentry.devicePixelContentBoxSize
-
-
targetDepthがshallowestTargetDepthより小さければ、shallowestTargetDepthをtargetDepthに設定する。
-
-
observerの
callbackをentriesで呼び出す。 -
observerの
activeTargetsをクリアする。
-
-
shallowestTargetDepthを返す。
3.4.6. Resize Loop Errorの通知
Resize Loop Error通知を行うには、次の手順を実行します:
-
新しい
ErrorEventを作成する。 -
eventのmessageスロットに"ResizeObserver loop completed with undelivered notifications."を設定する。
-
例外eventを報告する。
3.4.7. ノードの深さを計算
ノードの深さを計算は、与えられたnodeに対して次の手順を実行します:
-
pをnodeからこの要素のフラット化DOMツリーのルートElementへの親トラバーサルパスとする。
-
p内のノード数を返す。
3.4.8. targetと監視ボックスを与えてボックスサイズを計算
このアルゴリズムはtarget Elementの監視ボックスサイズを計算します。ボックスの種類はResizeObserverBoxOptionsで示されます。
SVG要素は例外です。SVGサイズは常にバウンディングボックスサイズであり、SVG要素は標準のCSSボックスモデルを使用しません。
ボックスサイズを計算は、targetとobservedBoxを与えて次の手順を実行します:
-
targetが
SVGGraphicsElementの場合-
computedSize.inlineSizeにtargetのバウンディングボックスのインライン長を設定する。
-
computedSize.blockSizeにtargetのバウンディングボックスのブロック長を設定する。
-
-
targetが
SVGGraphicsElementでない場合-
observedBoxが"border-box"の場合
-
computedSize.inlineSizeにtargetのborder areaのインライン長を設定する。
-
computedSize.blockSizeにtargetのborder areaのブロック長を設定する。
-
-
observedBoxが"content-box"の場合
-
computedSize.inlineSizeにtargetのcontent areaのインライン長を設定する。
-
computedSize.blockSizeにtargetのcontent areaのブロック長を設定する。
-
-
observedBoxが"device-pixel-content-box"の場合
-
computedSize.inlineSizeにtargetのcontent areaのインライン長(デバイスピクセル単位の整数値)を設定する。
-
computedSize.blockSizeにtargetのcontent areaのブロック長(デバイスピクセル単位の整数値)を設定する。
-
-
computedSizeを返す。
-
3.5. ResizeObserverのライフタイム
ResizeObserverは、以下の2つの条件が両方とも満たされるまで存続します:
-
オブザーバーへのスクリプト参照が存在しない。
-
オブザーバーがいずれのターゲットも監視していない。
3.6. 外部仕様との統合
3.6.1. HTML処理モデル:イベントループ
ResizeObserverの処理は、HTML処理モデルのイベントループのステップ7.12内で行われます。
ステップ12は現時点では以下のように明確に規定されていません:
docs内のすべての完全にアクティブなDocumentについて、そのDocumentおよびその閲覧コンテキストのレンダリングやユーザーインターフェイスを現在の状態に合わせて更新する。
現行のステップ12は、以下のように完全に規定できます:
docs内のすべての完全にアクティブなDocumentについて、そのDocumentおよびその閲覧コンテンツに対して以下の手順を実行します:
-
スタイル再計算
-
レイアウト更新
-
描画
ResizeObserverはステップ12にリサイズ通知処理を追加します。
保留中の通知がなくなるまでループしてすべての通知を配信しようとします。これにより無限ループが発生する可能性があります。
無限ループは、各イテレーションごとに通知可能なノードの集合を縮小することで防止されます。各イテレーションでは、前回のイテレーションで最も浅かったノードより深いノードのみが通知できます。
通知ループが完了した時点で未配信の通知がある場合はエラーが生成されます。未配信の通知がある要素は次回のループで通知対象となります。
ResizeObserverの通知を含むステップ12は次の通りです:
docs内のすべての完全にアクティブなDocumentについて、そのDocumentおよびその閲覧コンテキストに対して以下の手順を実行します:
-
スタイル再計算
-
レイアウト更新
-
depthを0に設定
-
指定した深さでアクティブな観測を収集 depthを
Documentで実行 -
documentがアクティブな観測があるかの間、繰り返す
-
depthにアクティブな観測の通知の結果を設定する。
-
スタイル再計算
-
レイアウト更新
-
指定した深さでアクティブな観測を収集 depthを
Documentで実行
-
-
Documentがスキップされた観測があるかの場合は、Resize Loop Error通知を実行する -
Documentおよびその閲覧コンテキストのレンダリングやユーザーインターフェイスを現在の状態に合わせて更新する。