Mount strategies
A mount strategy decides when the registry mounts an instance. It constructs nothing and it wraps nothing.
The seven values
| Strategy | Mounts when | Reversible |
|---|---|---|
eager (default) | the element enters the document | no |
visible[:<rootMargin>] | it first intersects the viewport | no |
in-view[:<rootMargin>] | it intersects the viewport | yes |
idle | the main thread becomes idle | no |
interaction | the user first aims at it | no |
interaction:page | the user first interacts with the page | no |
media:<query> | the query is not empty and it matches | yes |
One-shot and reversible are separate values. visible mounts once and stays. in-view mounts and unmounts on each crossing.
Declaring one
A component declares its default in its config; any element overrides it with data-mount:
import { Base } from '@studiometa/js-toolkit';
class Map extends Base {
static config = {
name: 'Map',
mountStrategy: 'visible',
};
}<!-- uses the component default -->
<div data-component="Map"></div>
<!-- overridden per element -->
<div data-component="Map" data-mount="eager"></div>
<div data-component="Map" data-mount="visible:200px"></div>
<div data-component="Chat" data-mount="interaction:page"></div>
<div data-component="Parallax" data-mount="media:(prefers-reduced-motion: no-preference)"></div>The resolution chain is:
data-mount > manifest entry mountStrategy > config.mountStrategy > 'eager'The grammar
The accepted values are exactly eager, visible, visible:, visible:<rootMargin>, in-view, in-view:, in-view:<rootMargin>, idle, interaction, interaction:, interaction:page and media:<non-empty query>.
- A viewport suffix is passed straight through as
IntersectionObserverInit.rootMargin. - The empty suffix behaves as the bare strategy.
pageis the only scope the interaction strategy takes.- There is no threshold, no root element, no JSON options and no second attribute.
An unknown value, an empty media query, or a rootMargin the browser refuses leaves the component unmounted and reports component.invalid-mount-strategy once. The inert controller stays current while the declaration does not change, so it is not reported again; a corrected attribute replaces it. A failed strategy cannot stop the rest of a reconciliation.
interaction — intent on the element
pointerenter, pointerdown and focusin, bound to the element itself, so the component mounts before the click arrives. Two of the three bubble, so an interaction anywhere inside the subtree counts; pointerenter fires for the root element only.
interaction:page — the visit, not the element
This is the deferred-widget case: a chat panel, an embed, a third-party script, where nothing about the element predicts the moment and any sign of a live user does.
- The events are the deliberate ones:
pointerdown,keydownandfocusin. Hovering is deliberately not one of them — aiming is intent for one element and noise for a document. - One listener set for the page, in the shared runtime, not one per waiting element. They attach with the first waiting element and release on the first interaction.
- They are captured, so a handler that stops propagation cannot hide the visit.
- The signal is a fact about the visit. An element that arrives after the interaction mounts at once — but only when the strategy was already listening, since nothing observes a page that has asked for nothing.
What a waiting component is
A component that waits has no instance. It is invisible to $query(), $closest(), $watchChildren() and getInstances(), and it announces nothing. There is nothing to guard against and nothing to check.
Once it has mounted and been stood down again — which only in-view and media: do — the instance exists and is unmounted, so it is what getUnmountedInstances() answers.
Lifetime
- Several components on one element share that element's
data-mount. A component that needs its own policy declares it in its config. - Teardown follows the element. A strategy is disposed when its element leaves the document. A move ends as an unmount and a mount of the same instance.
- The attribute is live. A change to
data-mountdisposes the old strategy and applies the final one. Controller identity guards a queued callback of a disposed strategy.
What this replaces
The withMountWhenInView, withMountOnMediaQuery and withMountWhenPrefersMotion decorators of v3 are deleted. A decorator that wraps a constructor to decide whether to mount is the registry's job, and the registry does it without touching the class.
data-load and loadStrategy are gone too: deferring the import and deferring the mount are one decision. See Autoloading.
withInView is a different thing
withInView observes a component that is already mounted and calls intersected(). It does not replace the visible or in-view mount strategy.