1. Introduction
This section is non-normative.
Responsive Web Components need to respond to content rect size changes. An example is an Element that displays a map:
-
it displays a map by tiling its content box with
Elementtiles. -
when resized, it must redo the tiling.
Responsive Web Applications can already respond to viewport size changes.
This is done with CSS media queries, or window.resize event.
The ResizeObserver API is an interface for observing changes
to Element's content rect’s width and height. It is an Element's
counterpart to window.resize event.
ResizeObserver’s notifications can be used to respond to changes in Element's size. Some interesting facts about these observations:
-
observation will fire when watched Element is inserted/removed from DOM.
-
observation will fire when watched Element display gets set to none.
-
observations do not fire for non-replaced inline Elements.
-
observations will not be triggered by CSS transforms.
-
observation will fire when observation starts if Element has display, and Element’s size is not 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">menu title</p> </div>
// In response to resize, elipse paints an elipse inside a canvas document.querySelector('#elipse').handleResize = entry => { entry.target.width = entry.contentRect.width; entry.target.height = entry.contentRect.height; let rx = Math.floor(entry.contentRect.width / 2); let ry = Math.floor(entry.contentRect.height / 2); let ctx = entry.target.getContext('2d'); ctx.beginPath(); ctx.ellipse(rx, ry, rx, ry, 0, 0, 2 * Math.PI); ctx.stroke(); } // In response to resize, change title visibility depending on width document.querySelector('#menu').handleResize = entry => { let title = entry.target.querySelector(".title") if (entry.contentRect.width < 40) title.style.display = "none"; else title.style.display = "inline-block"; } var ro = new ResizeObserver( entries => { for (let entry of entries) { let cs = window.getComputedStyle(entry.target); console.log('watching element:', entry.target); console.log(entry.contentRect.width,' is ', cs.width); console.log(entry.contentRect.height,' is ', cs.height); console.log(entry.contentRect.top,' is ', cs.paddingTop); console.log(entry.contentRect.left,' is ', cs.paddingLeft); 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 interface
The ResizeObserver interface is used to observe changes to Element's content rect.
It is modeled after MutationObserver and IntersectionObserver.
[Constructor(ResizeObserverCallback callback),
Exposed=Window]
interface ResizeObserver {
void observe(Element target);
void unobserve(Element target);
void disconnect();
};
- new ResizeObserver(callback)
-
-
Let this be a new
ResizeObserverobject. -
Set this internal
callbackslot to callback. -
Add this to
Document'sresizeObserversslot.
-
- observe(target)
-
Adds target to the list of observed elements.
-
If target is in
observationTargetsslot, return. -
Let resizeObservation be new
ResizeObservation(target). -
Add the resizeObservation to the
observationTargetsslot.
-
- unobserve(target)
-
Removes target from the list of observed elements.
-
Let observation be
ResizeObservationinobservationTargetswhose target slot is target. -
If observation is not found, return.
-
Remove observation from
observationTargets
-
- disconnect()
-
1) Clear the
observationTargetslist.2) Clear the
activeTargetslist.
2.2. ResizeObserverCallback
callback ResizeObserverCallback = void (sequence<ResizeObserverEntry> entries, ResizeObserver observer)
This callback delivers ResizeObserver's notifications. It is invoked by a broadcast active observations algorithm.
2.3. ResizeObserverEntry
[Constructor(Element target) ] interface ResizeObserverEntry { readonly attribute Element target; readonly attribute DOMRectReadOnly contentRect; };
- target, of type Element, readonly
-
The
Elementwhose size has changed. - contentRect, of type DOMRectReadOnly, readonly
-
Element's content rect whenResizeObserverCallbackis invoked.
- new ResizeObserverEntry(target)
-
-
Let this be a new
ResizeObserverEntry. -
Set this
targetslot to target. -
If target is not an SVG element do these steps:
-
Set this.contentRect.width to target.content width.
-
Set this.contentRect.height to target.content height.
-
Set this.contentRect.top to target.padding top.
-
Set this.contentRect.left to target.padding left.
-
-
If target is an SVG element do these steps:
-
Set this.contentRect.top and this.contentRect.left to 0.
-
Set this.contentRect.width to target.bounding box.width.
-
Set this.contentRect.height to target.bounding box.height.
-
-
2.4. ResizeObservation
ResizeObservation holds observation information for a singleElement. This
interface is not visible to Javascript.
[Constructor(Element target) ] interface ResizeObservation { readonly attribute Element target; readonly attribute float broadcastWidth; readonly attribute float broadcastHeight; boolean isActive(); };
- target, of type Element, readonly
-
The observed
Element. - broadcastWidth, of type float, readonly
-
Width of last broadcast content width.
- broadcastHeight, of type float, readonly
-
Width of last broadcast content height.
- new ResizeObservation(target)
-
-
Let this be a new
ResizeObservationobject -
Set this internal
targetslot to target -
Set this
broadcastWidthslot to 0. -
Set this
broadcastHeightslot to 0.
-
- isActive()
-
-
If
targetis an HTML element do these steps:-
If
target.content width !=broadcastWidthreturn true. -
If
target.content height !=broadcastHeightreturn true.
-
-
If
targetis an SVGGraphicsElement do these steps:-
If
target.bounding box width !=broadcastWidthreturn true. -
If
target.bounding box height !=broadcastHeightreturn true.
-
-
return false.
-
3. Processing Model
3.1. Internal Slot Definitions
3.1.1. Document
Document has a resizeObservers slot that is a list of ResizeObservers in this document. It is initialized to empty.
3.1.2. ResizeObserver
ResizeObserver has a callback slot, initialized by constructor.
ResizeObserver has an observationTargets slot, which is a list of ResizeObservations.
It represents all Elements being observed.
ResizeObserver has a activeTargets slot, which is a list of ResizeObservations. It represents all Elements whose size has changed since last observation broadcast that are eligible for broadcast.
ResizeObserver has a skippedTargets slot, which is a list of ResizeObservations. It represents all Elements whose size has changed since last observation broadcast that are **not** eligible for broadcast
3.2. CSS Definitions
3.2.1. content rect
DOM content rect is a rect whose:-
width is content width
-
height is content height
-
top is padding top
-
left is padding left
content width spec does not mention how multi-column layout affects content box. In this spec, content width of an Element inside multi-column is the result of getComputedStyle(Element).width. This currently evaluates to width of the first column.
Having content rect position be padding-top/left is useful for absolute positioning of target’s children. Absolute position coordinate space origin is topLeft of the padding rect.
Watching content rect means that:
-
observation will fire when watched Element is inserted/removed from DOM.
-
observation will fire when watched Element display gets set to none.
-
non-replaced inline Elements will always have an empty content rect.
-
observations will not be triggered by CSS transforms.
Web content can also contain SVG elements. SVG Elements define bounding box instead of a content box. Content rect for SVGGraphicsElements is a rect whose:
-
width is bounding box width
-
height is bounding box height
-
top and left are 0
3.3. Algorithms
3.3.1. Gather active observations at depth
It computes all active observations for a document. To gather active observations at depth, run these steps:
-
Let depth be the depth passed in.
-
For each observer in
resizeObserversrun these steps:-
Clear observer’s
activeTargets, andskippedTargets -
For each observation in observer.
observationTargetsrun this step:-
If observation.
isActive()is true-
Let targetDepth be result of calculate depth for node for observation.
target. -
If targetDepth is greater than depth then add observation to
activeTargets. -
Else add observation to
skippedTargets.
-
-
-
3.3.2. Has active observations
To determine if Document has active observations run these steps:
-
For each observer in
resizeObserversrun this step:-
If observer.
activeTargetsis not empty, return true.
-
-
return false.
3.3.3. Has skipped observations
To determine if Document has skipped observations run these steps:
-
For each observer in
resizeObserversrun this step:-
If observer.
skippedTargetsis not empty, return true.
-
-
return false.
3.3.4. Broadcast active observations
broadcast active observations delivers all active observations in a document, and returns the depth of the shallowest broadcast target depth.
To broadcast active observations for a document, run these steps:
-
Let shallowestTargetDepth be ∞
-
For each observer in document.
resizeObserversrun these steps:-
If observer.
activeTargetsslot is empty, continue. -
Let entries be an empty list of
ResizeObserverEntryies. -
For each observation in
activeTargetsperform these steps:-
Let entry be new
ResizeObserverEntry(observation.target) -
Add entry to entries
-
Set observation.
broadcastWidthto entry.contentRect.width. -
Set observation.
broadcastHeightto entry.contentRect.height. -
Set targetDepth to the result of calculate depth for node for observation.
target. -
Set shallowestTargetDepth to targetDepth if targetDepth < shallowestTargetDepth
-
-
Invoke observer.
callbackwith entries. -
Clear observer.
activeTargets.
-
-
Return shallowestTargetDepth.
3.3.5. Deliver Resize Loop Error
To deliver resize loop error notification run these steps:
-
Create a new
ErrorEvent. -
Initialize event’s message slot to "ResizeObserver loop completed with undelivered notifications.".
-
Dispach the event to document’s window.
3.3.6. Calculate depth for node
To calculate depth for node node run these steps:
-
Let p be the parent-traversal path from node to a root Element of this element’s DOM tree.
-
Return number of nodes in p.
3.4. ResizeObserver Lifetime
A ResizeObserver will remain alive until both of these conditions are met:
-
there are no scripting references to the observer.
-
the observer is not observing any targets.
3.5. External Spec Integrations
3.5.1. HTML Processing Model: Event Loop
ResizeObserver processing happens inside the step 7.12 of the HTML Processing Model event loop.
Step 12 is currently underspecified as:
For each fully active Document in docs, update the rendering or user interface of that Document and its browsing context to reflect the current state.
.
Existing step 12 can be fully specified as:
For each fully active Document in docs, run the following steps for that Document and its browsing contents:
-
recalc styles
-
update layout
-
paint
ResizeObserver extends step 12 with resize notifications.
It tries to deliver all pending notifications by looping
until no pending notifications are available. This can cause
an infinite loop.
Infinite loop is prevented by shrinking the set of nodes that can notify at every iteration. In each iteration, only nodes deeper than the shallowest node in previous iteration can notify.
An error is generated if notification loop completes, and there are undelivered notifications. Elements with undelivered notifications will be considered for delivery in the next loop.
Step 12 with ResizeObserver notifications is:
For each fully active Document in docs, run the following steps for that Document and its browsing contentx:
-
recalc styles
-
update layout
-
set depth to 0
-
gather active observations at depth depth for
Document -
repeat while (document has active observations)
-
set depth to broadcast active observations
-
recalc styles
-
update layout
-
gather active observations at depth depth for
Document
-
-
if
Documenthas skipped observations then deliver resize loop error notification -
update the rendering or user interface of
Documentand its browsing context to reflect the current state.