Skip to content

Commit ea83970

Browse files
DavMilatabatkins
andauthored
[css-scroll-snap-2] Add SnapEvent definition (w3c#9515)
Co-authored-by: Tab Atkins Jr <jackalmage@gmail.com>
1 parent 919aff7 commit ea83970

File tree

2 files changed

+247
-102
lines changed

2 files changed

+247
-102
lines changed

css-scroll-snap-2/Overview.bs

Lines changed: 232 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ Snap Events {#snap-events}
262262
████████ ███ ████████ ██ ██ ██ ██████
263263
-->
264264

265-
{{snapChanged}} and {{snapChanging}} {#snapchanged-and-snapchanging}
265+
{{snapchanged}} and {{snapchanging}} {#snapchanged-and-snapchanging}
266266
--------------------------------------------
267267

268268
CSS scroll snap points are often used as a mechanism to
@@ -275,119 +275,250 @@ Snap Events {#snap-events}
275275
<table class="data" id="eventhandlers">
276276
<thead>
277277
<tr>
278-
<th>Event handler
279-
<th>Event handler event type
278+
<th>Event</th>
279+
<th>Interface</th>
280+
<th>Targets</th>
281+
<th>Description</th>
282+
</tr>
283+
</thead>
280284
<tbody>
281285
<tr>
282-
<th><dfn event>snapChanged</dfn>
283-
<td>{{scroll!!event}}
286+
<th><dfn for="snapchanged" event>snapchanged</dfn></th>
287+
<td>{{SnapEvent}}</td>
288+
<td>scroll containers</td>
289+
<td>Fired at the scrolling element or {{Document}} at the end of a scroll (before a {{scrollend}} event)
290+
or after a <a href="https://drafts.csswg.org/css-scroll-snap-1/#re-snap">layout snap</a>
291+
if the element that the scrolling element or Document is snapped to changed.</td>
292+
</tr>
284293
<tr>
285-
<th><dfn event>snapChanging</dfn>
286-
<td>{{scroll!!event}}
294+
<th><dfn for="snapchanging" event>snapchanging</dfn></th>
295+
<td>{{SnapEvent}}</td>
296+
<td>scroll containers</td>
297+
<td>Fired at the scrolling element or {{Document}} during scrolling (before a {{scroll}} event),
298+
if the element that the scrolling would cause the scroller to snap to is
299+
different from the target reported by the last snapchanging event that was fired.</td>
300+
</tr>
301+
</tbody>
287302
</table>
288-
<h4>SnapEvents</h4>
289-
<pre class="idl">
290-
[Exposed=Window]
291-
interface SnapEvent : Event {
292-
constructor(DOMString type, optional SnapEventInit eventInitDict = {});
293-
readonly attribute EventTarget? target;
294-
readonly attribute SnapTargetList snappedTargets;
295-
readonly attribute SnapTargetList snapTargets;
296-
readonly attribute boolean invokedProgrammatically;
297-
readonly attribute boolean smoothlyScrolled;
298-
};
299303

300-
[Exposed=Window]
301-
interface SnapTargetList {
302-
readonly attribute SnapTargetArray x;
303-
readonly attribute SnapTargetArray y;
304+
<h4 for="snapchanged" id="snapchanged"> snapchanged </h4>
305+
{{snapchanged}} indicates that the snap area to which a snap container is snapped along either axis has changed.
306+
{{snapchanged}} is dispatched:
307+
308+
<ol>
309+
<li>
310+
when a scrolling operation is <a spec="cssom-view-1" lt="scroll completed">completed</a>
311+
if, for either the block or inline axis, the
312+
element which the snap container is snapped to is different from the element
313+
it most recently snapped to in that axis. For snap containers with
314+
''scroll-snap-type/proximity'' strictness, the scroll may result in the snap
315+
container no longer being snapped to any element. [[css-scroll-snap-1#choosing]]
316+
describes the method a UA follows when choosing between elements which are
317+
<a spec="css-scroll-snap-1" lt="scroll snap area">snap areas</a>.
318+
</li>
319+
<li> if there is a change to a snap container's style such that it goes from
320+
having a non-'none' value for [[css-scroll-snap-1#scroll-snap-type|scroll-snap-type]]
321+
to having a 'none' value or vice versa.
322+
</li>
323+
<li> if, after a [[css-scroll-snap-1#re-snap|layout change]], the element to
324+
which a snap container is snapped to changes, regardless of whether there is
325+
a change in scroll position after the layout change.
326+
</li>
327+
</ol>
328+
329+
Scrolling operations always lead to {{scrollend}} events being fired. If a
330+
scrolling operation also results in a {{snapchanged}} event being fired, the
331+
{{snapchanged}} event should be fired before the {{scrollend}} event.
332+
333+
Each {{Document}} has an associated list of
334+
<dfn export for=Document>pending snapchanged event targets</dfn>, initially empty.
335+
336+
Each
337+
<a spec=css-scroll-snap lt="scroll snap container">snap container</a> has
338+
one <dfn export>snapchangedTargetBlock</dfn> and one
339+
<dfn export>snapchangedTargetInline</dfn> in the block and inline axes
340+
respectively, each of which can either be null if the container is not
341+
snapped in that axis or the {{Element}} to which the container is snapped.
342+
343+
When asked to <dfn export for=Document>update snapchanged targets</dfn>
344+
for a <a spec=css-scroll-snap lt="scroll snap container">snap container</a>,
345+
|snapcontainer|, run these steps:
346+
347+
1. Let <var>doc</var> be |snapcontainer|'s associated {{Document}}.
348+
1. Let <var>blockTarget</var> be the <a>snapchangedTargetBlock</a> associated
349+
with |snapcontainer|.
350+
1. Let <var>inlineTarget</var> be the <a>snapchangedTargetInline</a> associated
351+
with |snapcontainer|.
352+
1. Let <var>blockSnapchangingTarget</var> be the <a>snapchangingTargetBlock</a>
353+
associated with |snapcontainer|.
354+
1. Let <var>inlineSnapchangingTarget</var> be the
355+
<a>snapchangingTargetInline</a> associated with |snapcontainer|.
356+
1. Let <var>snap targets changed</var> be a boolean flag that is initially false.
357+
1. If <var>blockTarget</var> is not the same element as <var>blockSnapchangingTarget</var> or
358+
1. Set the <a>snapchangedTargetBlock</a> associated with |snapcontainer| to
359+
<var>blockSnapchangingTarget</var>.
360+
1. Set <var>snap targets changed</var> to true.
361+
1. If <var>inlineTarget</var> is not the same element as <var>inlineSnapchangingTarget</var>:
362+
1. Set the <a>snapchangedTargetInline</a> associated with |snapcontainer| to
363+
<var>inlineSnapchangingTarget</var>.
364+
1. Set <var>snap targets changed</var> to true.
365+
1. If <var>snap targets changed</var> is true:
366+
1. If |snapcontainer| is not already in <var>doc</var>'s
367+
<a>pending snapchanged event targets</a>:
368+
1. Append |snapcontainer| to <var>doc</var>'s
369+
<a>pending snapchanged event targets</a>.
370+
371+
Note: When snapping occurs on a scroller (either due to a layout change or a
372+
scrolling operation) the <a>snapchangingTargetBlock</a> and <a>snapchangingTargetInline</a>
373+
associated with that scroller are updated and represent the current snap targets
374+
of that scroller. This allows the <a>update snapchanged targets</a> algorithm
375+
to use these elements to determine whether a {{snapchanged}} event should be fired.
376+
377+
When asked to <dfn>dispatch pending snapchanged events</dfn> for a {{Document}},
378+
<var>doc</var>, run these steps:
379+
1. For each item <var>target</var> in |doc|'s <a>pending snapchanged event targets</a>:
380+
1. Fire a {{SnapEvent}}, |snapevent|, named {{snapchanged}} at <var>target</var>
381+
and let |snapevent|'s {{SnapEvent/snapTargetBlock}} and
382+
{{SnapEvent/snapTargetInline}} attributes be the <a>snapchangedTargetBlock</a> and the
383+
<a>snapchangedTargetInline</a>, respectively, that are associated with <var>target</var>.
384+
1. Empty <var>doc</var>'s <a>pending snapchanged event targets</a>.
385+
386+
<h4 id="snapchanging"> snapchanging </h4>
387+
{{snapchanging}} is dispatched:
388+
* during a scrolling operation, if the element to which a
389+
<a spec=css-scroll-snap lt="scroll snap container">snap container</a> would
390+
<a spec="css-scroll-snap-1" lt="scroll snap">snap</a> (in either axis) changes, or
391+
* if a [[css-scroll-snap-1#re-snap|layout change]] occurs such that a {{snapchanged}} event
392+
is to be dispatched. In this case, as with the scrolling case, the {{snapchanging}} event
393+
should be dispatched before the {{snapchanged}} event.
394+
395+
A scrolling operation might animate towards a particular position (e.g.
396+
scrollbar arrow clicks, arrow key presses, "behavior: smooth" programmatic
397+
scrolls) or might directly track a user's input (e.g. touch scrolling, scrollbar
398+
dragging). In either case, the user agent [[css-scroll-snap-1#choosing|chooses]] an
399+
<dfn>eventual snap target</dfn> in each axis to which the scroller will
400+
<a spec="css-scroll-snap-1" lt="scroll snap">snap</a> after the scrolling operation
401+
reaches its intended scroll position.
402+
* In the former case, the intended scroll position is the scroll animation's
403+
target scroll offset.
404+
* In the latter case, the intended scroll position is the current scroll offset as
405+
determined by the user's input.
406+
407+
{{snapchanging}} aims to let the web page know, as early as possible,
408+
that the scrolling operation will result in a change in the element the snap
409+
container is snapped to. The user agent should evaluate whether to trigger
410+
{{snapchanging}} based on the <a>eventual snap target</a> to which the scroller would
411+
<a spec="css-scroll-snap-1" lt="scroll snap">snap</a> were the scrolling operation
412+
to reach its intended scroll position.
413+
414+
Note: Since snapchanging gives the web page hints about future snapping,
415+
the snapping hinted at by a snapchanging event might not materialize since it
416+
will be possible for subsequent scrolling input to further alter the snap
417+
container's scroll position and result in a different eventual snap target.
418+
419+
420+
{{snapchanging}} events are fired before {{scroll}} events.
421+
422+
Each {{Document}} has an associated list of
423+
<dfn export for=Document>pending snapchanging event targets</dfn>, initially empty.
424+
425+
Each
426+
<a spec=css-scroll-snap lt="scroll snap container">snap container</a> has
427+
one <dfn>snapchangingTargetBlock</dfn>
428+
and one <dfn>snapchangingTargetInline</dfn>in the block and inline axes
429+
respectively, each of which can either be null if the container is not
430+
snapping in that axis or the {{Element}} to which the container is snapping.
431+
432+
When asked to <dfn export for=Document>update snapchanging targets</dfn>
433+
for a <a spec=css-scroll-snap lt="scroll snap container">snap container</a>,
434+
|snapcontainer|, given an {{Element}} newBlockTarget and an {{Element}}
435+
newInlineTarget, run these steps:
436+
437+
1. Let <var>doc</var> be |snapcontainer|'s associated {{Document}}.
438+
1. Let <var>blockTarget</var> be the <a>snapchangingTargetBlock</a> that is
439+
associated with |snapcontainer|.
440+
1. Let <var>inlineTarget</var> be the <a>snapchangingTargetInline</a> that is
441+
associated with |snapcontainer|.
442+
1. If <var>newBlockTarget</var> is not the same element as <var>blockTarget</var>:
443+
1. Set the <a>snapchangingTargetBlock</a> associated with |snapcontainer| to
444+
<var>newBlockTarget</var>.
445+
1. If |snapcontainer| is not already in <var>doc</var>'s
446+
<a>pending snapchanging event targets</a>,
447+
1. Append |snapcontainer| to <var>doc</var>'s
448+
<a>pending snapchanging event targets</a>
449+
1. If <var>newInlineTarget</var> is not the same element as <var>inlineTarget</var>:
450+
1. Set the <a>snapchangingTargetInline</a> associated with |snapcontainer| to
451+
<var>newInlineTarget</var>.
452+
1. If |snapcontainer| is not already in <var>doc</var>'s
453+
<a>pending snapchanging event targets</a>,
454+
1. Append |snapcontainer| to <var>doc</var>'s
455+
<a>pending snapchanging event targets</a>.
456+
457+
When asked to <dfn>dispatch pending snapchanging events</dfn> for a {{Document}},
458+
<var>doc</var>, run these steps:
459+
1. For each item <var>target</var> in |doc|'s <a>pending snapchanging event targets</a>:
460+
1. Fire a {{SnapEvent}}, |snapevent|, named {{snapchanging}} at <var>target</var>
461+
and let |snapevent|'s {{SnapEvent/snapTargetBlock}} and
462+
{{SnapEvent/snapTargetInline}} attributes be the <a>snapchangingTargetBlock</a> and the
463+
<a>snapchangingTargetInline</a>, respectively, that are associated with <var>target</var>.
464+
1. Empty <var>doc</var>'s <a>pending snapchanging event targets</a>.
465+
466+
<h4 id="snap-events-on-layout-changes">Snap Events due to Layout Changes </h4>
467+
When a <a spec=css-scroll-snap lt="scroll snap container">snap container</a>,
468+
|snapcontainer|, [[css-scroll-snap-1#re-snap|re-snaps]], run these steps:
469+
470+
1. Let <var>newBlockTarget</var> be the element that |snapcontainer| has
471+
<a spec="css-scroll-snap-1" lt="scroll snap">snapped</a> to
472+
in the block axis or null if it did not snap to any element.
473+
1. Let <var>newInlineTarget</var> be the element that |snapcontainer| has
474+
<a spec="css-scroll-snap-1" lt="scroll snap">snapped</a> to
475+
in the inline axis or null if it did not snap to any element.
476+
1. Run the steps to <a>update snapchanging targets</a> with <var>newBlockTarget</var>
477+
as newBlockTarget and <var>newInlineTarget</var> as newInlineTarget.
478+
1. Run the steps to <a>update snapchanged targets</a> for |snapcontainer|.
479+
480+
481+
SnapEvent interface
482+
-------------------
483+
484+
<pre class="idl">
485+
dictionary SnapEventInit : EventInit {
486+
Node? snapTargetBlock;
487+
Node? snapTargetInline;
304488
};
305489

306490
[Exposed=Window]
307-
interface SnapTargetArray {
308-
readonly attribute unsigned long length;
309-
getter EventTarget? item (unsigned long index);
491+
interface SnapEvent : Event {
492+
constructor(DOMString type, optional SnapEventInit eventInitDict = {});
493+
readonly attribute Node? snapTargetBlock;
494+
readonly attribute Node? snapTargetInline;
310495
};
496+
</pre>
311497

312-
dictionary SnapEventInit : EventModifierInit {
313-
sequence&lt;EventTarget> snappedTargetsX = [];
314-
sequence&lt;EventTarget> snappedTargetsY = [];
315-
sequence&lt;EventTarget> snapTargetsListX = [];
316-
sequence&lt;EventTarget> snapTargetsListY = [];
317-
};
318-
</pre>
498+
<dl>
499+
<div dfn-type=attribute class=attributes dfn-for="SnapEvent">
500+
: <dfn>snapTargetBlock</dfn>
501+
::
502+
The element that the snap container is snapped to in the block axis
503+
at the <a spec="css-scroll-snap-1" lt="scroll snap position">snap position</a>
504+
for the associated snap event.
505+
</div>
506+
<div dfn-type=attribute class=attributes dfn-for="SnapEvent">
507+
: <dfn>snapTargetInline</dfn>
508+
::
509+
The element that the snap container is snapped to in the inline axis
510+
at the <a spec="css-scroll-snap-1" lt="scroll snap position">snap position</a>
511+
for the associated snap event.
512+
</div>
513+
514+
For {{snapchanged}} events, the snap position is the position already
515+
realized by the snap container after a scroll snap. For {{snapchanging}}
516+
events it is the snap position that the snap container will eventually
517+
snap to when the scrolling operation ends.
319518

320-
<dl>
321-
<dt><code>SnapEvent . target</code></dt>
322-
<dd>
323-
This is the scroll container of the snapped-to element.
324-
</dd>
325-
<dt><code>SnapEvent . snappedTargets</code></dt>
326-
<dd>
327-
An object with 2 keys for each axis, each key returns an array of snapped targets.
328-
</dd>
329-
<dt><code>SnapEvent . snapTargets</code></dt>
330-
<dd>
331-
An object with 2 keys for each axis, each key returns an array of the aggregated snap children.
332-
</dd>
333-
<dt><code>SnapEvent . invokedProgrammatically</code></dt>
334-
<dd>
335-
A boolean informing developers if a user or script invoked scroll that caused <a>snapChanged</a>.
336-
</dd>
337-
<dt><code>SnapEvent . smoothlyScrolled</code></dt>
338-
<dd>
339-
A boolean informing developers if the snap change was instant or interpolated.
340-
</dd>
341519
</dl>
342520

343-
<h4> snapChanged </h4>
344-
345-
The event is dispatched when a new snap target has been snapped to, providing what caused it.
346-
It should be dispatched:
347-
348-
* if user scroll interaction has ended and a new item has been rested on. If a user is still touching the screen or the touchpad, this event should not fire, even if the scroll position is exactly at a snapped element's position.
349-
* if animations or transitions change the snapped style of the container or children, IF they have in fact changed the snap target.
350-
351-
<table>
352-
<tr><th>Type</th><td><strong><code>snapChanged</code></strong></td></tr>
353-
<tr><th>Interface</th><td>{{SnapEvent}}</td></tr>
354-
<tr><th>Sync / Async</th><td>Async</td></tr>
355-
<tr><th>Bubbles</th><td>Yes</td></tr>
356-
<tr><th>Trusted Targets</th><td><code>Element</code></td></tr>
357-
<tr><th>Cancelable</th><td>No</td></tr>
358-
<tr><th>Composed</th><td>Yes</td></tr>
359-
<tr><th>Default action</th><td>None</td></tr>
360-
<tr><th>Context<br/>(trusted events)</th><td><ul>
361-
<li>{{Event}}.{{Event/target}} : scroll container of the snapped-to element</li>
362-
<li>{{SnapEvent}}.{{snappedTargets}} : an object with 2 keys for each axis, each key returns an array of snapped targets</li>
363-
<li>{{SnapEvent}}.{{snapTargets}} : an object with 2 keys for each axis, each key returns an array of the aggregated snap children</li>
364-
<li>{{SnapEvent}}.{{invokedProgrammatically}} : a boolean informing developers if a user or script invoked scroll that caused <a>snapChanged</a></li>
365-
<li>{{SnapEvent}}.{{smoothlyScrolled}} : a boolean informing developers if the snap change was instant or interpolated</li>
366-
</ul></td></tr>
367-
</table>
368-
369-
<h4> snapChanging </h4>
370-
371-
Should fire every time, and as soon as, the UA has determined a new snap child until the new child is snapped to.
372-
373-
<table>
374-
<tr><th>Type</th><td><strong><code>snapChanging</code></strong></td></tr>
375-
<tr><th>Interface</th><td>{{SnapEvent}}</td></tr>
376-
<tr><th>Sync / Async</th><td>Async</td></tr>
377-
<tr><th>Bubbles</th><td>Yes</td></tr>
378-
<tr><th>Trusted Targets</th><td><code>Element</code></td></tr>
379-
<tr><th>Cancelable</th><td>No</td></tr>
380-
<tr><th>Composed</th><td>Yes</td></tr>
381-
<tr><th>Default action</th><td>None</td></tr>
382-
<tr><th>Context<br/>(trusted events)</th><td><ul>
383-
<li>{{Event}}.{{Event/target}} : scroll container of the snapped-to element.</li>
384-
<li>{{SnapEvent}}.{{snappedTargets}}
385-
<li>{{SnapEvent}}.{{snapTargets}} : an object with 2 keys for each axis, each key returns an array of the aggregated snap children.</li>
386-
<li>{{SnapEvent}}.{{invokedProgrammatically}} : a boolean informing developers if a user or script invoked scroll that caused <a>snapChanged.</a></li>
387-
<li>{{SnapEvent}}.{{smoothlyScrolled}} : a boolean informing developers if the snap change was instant or interpolated.</li>
388-
</ul></td></tr>
389-
</table>
390-
521+
A {{SnapEvent}} should not bubble and should not be cancellable.
391522
<!--
392523
██ ███████ ██ ██ ██████ ██ ██ ███ ██ ██ ████████ ██████
393524
██ ██ ██ ███ ██ ██ ██ ██ ██ ██ ██ ███ ██ ██ ██ ██ ██

0 commit comments

Comments
 (0)