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
およびその閲覧コンテキストのレンダリングやユーザーインターフェイスを現在の状態に合わせて更新する。