CSS 滚动捕捉模块第 2 级

W3C 第一版公开工作草案

有关本文件的更多细节
此版本:
https://www.w3.org/TR/2024/WD-css-scroll-snap-2-20240723/
最新发布版本:
https://www.w3.org/TR/css-scroll-snap-2/
编辑草案:
https://drafts.csswg.org/css-scroll-snap-2/
历史:
https://www.w3.org/standards/history/css-scroll-snap-2/
实施报告:
https://wpt.fyi/results/css/css-scroll-snap
反馈:
CSSWG 问题存储库
规范内联问题
编辑:
Tab Atkins-Bittner (谷歌)
Elika J. Etemad / fantasai (苹果)
Adam Argyle (谷歌)
前任编辑:
Matt Rakow (微软)
Jacob Rossi (微软)
为此规范建议修改:
GitHub 编辑器

摘要

本模块包含控制平移和滚动行为的“捕捉位置”功能。

CSS 是描述结构化文档(如 HTML 和 XML)在屏幕、纸张等上呈现的语言。

本文件状态

本节描述了本文件在发布时的状态。当前的 W3C 发布列表和此技术报告的最新修订可在 W3C 技术报告索引 https://www.w3.org/TR/ 中找到。

本文件由 CSS 工作组 作为 第一版公开工作草案 发布,采用 推荐流程。作为第一版公开工作草案的发布并不意味着得到 W3C 及其成员的认可。

这是一个草案文件,可能会随时被其他文件更新、替换或废弃。不适宜将此文件引用为进展以外的内容。

请通过 在 GitHub 上提交问题(优选方式)发送反馈,在标题中包含规范代码“css-scroll-snap”,格式如下:“[css-scroll-snap] …评论摘要…”。所有问题和评论都将被 存档。另外,反馈也可以通过(存档)公共邮件列表 www-style@w3.org 发送。

本文件受 2023年11月03日 W3C 过程文件 管辖。

本文件由在 W3C 专利政策 下运作的一个小组制作。W3C 维护一个 公开的专利披露清单,其中列出了与小组交付成果相关的任何专利披露;该页面还包括披露专利的说明。任何对某项专利有实际了解的人,如果认为该专利包含 实质性主张,必须按照 W3C 专利政策第 6 节 披露信息。

在 CR 期间将产生测试套件和实施报告。

1. 引言

本节不是规范性内容。

这是目前相对于滚动捕捉 1 的一个增量规范。

滚动体验并不总是从开头开始。与轮播、滑动控制和列表视图的交互往往意图从某个元素开始,这个元素可能并不位于滚动容器的开头。需要使用 JavaScript 使滚动容器最初滚动到该元素。通过使 CSS 能够指定一个元素应当初始滚动到的位置,用户、页面作者和浏览器都能受益。

除了设置初始滚动目标外,开发者还需要对滚动捕捉进行洞察和事件监控。事件包括哪个元素在哪个轴上被捕捉、何时捕捉事件正在变化、何时捕捉完成,以及方便地以编程方式捕捉子元素。

1.1. 首次布局

该事件应遵循动画代码路径。当动画对象被创建并触发事件时,这就是一个框的首次布局。

2. 激励示例

一个从中间图像开始滚动的轮播:
.carousel {
    overflow-inline: auto;
    }
.carousel .origin {
    scroll-start-target: auto;
    }
<div class="carousel">
    <img src="img1.jpg">
    <img src="img2.jpg">
    <img src="img3.jpg" class="origin">
    <img src="img4.jpg">
    <img src="img5.jpg">
    </div>
当用户滚动回顶部时,搜索栏可用:
.scrollport {
    overflow-block: auto;
    }
    
main {
    scroll-start-target: auto;
    }
<div class="scrollport">
    <nav>
        ...
    </nav>
    <main>
        ...
    </main>
    </div>

3. 设置滚动开始位置

3.1. scroll-start-target 属性

3.1.1. 初始滚动目标

初始滚动 目标 是一个 滚动容器 scrollcontainer 的元素或伪元素, 其 'scroll-start-target' 属性不为 none, 并且其最近的 滚动容器scrollcontainer。 当存在多个此类元素或伪元素时,用户代理应选择在 树序 中出现的第一个。 如果不存在此类元素或伪元素,则 scrollcontainer初始滚动目标 为 null。

如果一个 初始 滚动目标滚动 容器 不为 null, 则应使用该目标来确定 scrollcontainer初始滚动位置,并按照以下步骤执行:
  1. target初始滚动目标

  2. position 为运行以下步骤的结果:确定滚动进入视图位置, 对于 target 设置 behavior 为 "auto",block 设置为 "start",inline 设置为 "nearest",并且 scrolling box 设置为 scrollcontainer

  3. scrollcontainer初始滚动位置 设置为 position

3.1.2. scroll-start-target 属性定义

名称: scroll-start-target
值: [ none | auto ]
初始: none
适用: 所有元素
继承:
百分比: 不适用
计算值: 见各个属性
规范顺序: 按语法
动画类型:
none
该元素不是 初始滚动目标
auto
该元素可能是其最近的 初始滚动目标,用于其最近的 滚动容器 祖先。

3.1.3. place-content 的交互

如果一个 滚动容器的 初始滚动位置 可能由内容分发属性和 scroll-start-target 在后代中设置,则 scroll-start-target 优先。

3.1.4. 首次布局后的到达

在文档被 更新 时, 滚动容器的 初始滚动目标 可能在该 滚动容器 已布局后到达。 如果发生这种情况, 用户代理仍应滚动到 初始滚动目标,除非用户代理有理由相信 用户不再对滚动到 初始滚动位置 感兴趣。

4. 样式化已捕捉项

':snapped' 伪类正在被淘汰,取而代之的是容器状态查询 方法。

4.1. 已捕捉元素伪类::snapped

:snapped 伪类匹配任何滚动捕捉目标, 无论轴向如何。 详细的物理和逻辑伪类选择器 允许更精细的捕捉子元素样式, 因为它们可以针对特定轴进行选择。

更具体的选项定义如下:

:snapped-x
匹配在水平轴上已捕捉的子元素。
:snapped-y
匹配在垂直轴上已捕捉的子元素。
:snapped-inline
匹配在行内轴上已捕捉的子元素。
:snapped-block
匹配在轴上已捕捉的子元素。

注意: 问题 #6985
需要 确定初始帧的解决方案。

5. 捕捉事件

5.1. scrollsnapchangescrollsnapchanging

CSS 滚动捕捉点通常用作 创建滚动交互“选择”组件的机制, 选择通过 JavaScript 交叉观察者 和滚动结束估算来确定。通过创建内置事件, 隐藏状态将变为可操作, 在合适的时机,并始终正确。

5.1.1. 捕捉目标

一个 捕捉目标 是一个元素 或伪元素,用户代理已选择捕捉 给定的捕捉容器

事件 接口 目标 描述
scrollsnapchange SnapEvent 滚动容器 在滚动元素或文档 的滚动结束时触发(在scrollend 事件之前) 或在布局捕捉之后触发,如果捕捉目标与 滚动元素或文档所捕捉的目标发生了变化。
scrollsnapchanging SnapEvent 滚动容器 在滚动元素或文档 滚动期间触发(在scroll 事件之前), 如果捕捉目标 导致滚动器捕捉的目标与上一个 滚动捕捉更改事件触发时所选择的捕捉目标不同。

5.1.2. scrollsnapchange

scrollsnapchange 表示快照容器在任一轴线上快照的快照区域已发生变化。scrollsnapchange 在以下情况下被派发:

  1. 当滚动操作完成时,如果在块轴或行内轴上,快照容器所快照的快照目标与该轴上最近快照的快照目标不同。对于具有邻近严格性的快照容器,滚动可能导致快照容器不再快照到任何快照目标CSS Scroll Snap 1 § 6.2 选择快照位置描述了用户代理在选择元素或伪元素时所遵循的方法,这些元素或伪元素是快照区域
  2. 如果快照容器的样式发生变化,使其从具有非值的scroll-snap-type变为有值,或者反之亦然。
  3. 如果在布局变化后,快照容器所快照的快照目标发生变化,无论布局变化后滚动位置是否发生变化。

滚动操作总是会导致触发scrollend 事件。如果一次滚动操作还导致触发scrollsnapchange 事件,则scrollsnapchange 事件应在scrollend 事件之前触发。

每个Document 都有一个关联的待处理的scrollsnapchange 事件目标列表,初始为空。

每个快照容器在块轴和行内轴上各有一个scrollsnapchangeTargetBlock和一个scrollsnapchangeTargetInline,每个可以是null,如果容器在该轴上未被快照,或者是快照目标,即容器所快照的目标。

当被要求为快照容器snapcontainer更新 scrollsnapchange 目标时,执行以下步骤:

  1. doc成为snapcontainer的关联Document

  2. blockTarget成为与snapcontainer关联的scrollsnapchangeTargetBlock

  3. inlineTarget成为与snapcontainer关联的scrollsnapchangeTargetInline

  4. blockScrollSnapchangingTarget成为与snapcontainer关联的scrollsnapchanging block-axis target

  5. inlineScrollSnapchangingTarget成为与snapcontainer关联的scrollsnapchanging inline-axis target

  6. snap targets changed成为一个初始为false的布尔标志。

  7. 如果blockTargetblockScrollSnapchangingTarget不同快照目标,或者

    1. 将与snapcontainer关联的scrollsnapchangeTargetBlock设置为blockScrollSnapchangingTarget

    2. snap targets changed为true。

  8. 如果inlineTargetinlineScrollSnapchangingTarget不同快照目标,或者

    1. 将与snapcontainer关联的scrollsnapchangeTargetInline设置为inlineScrollSnapchangingTarget

    2. snap targets changed为true。

  9. 如果snap targets changed为true:

    1. 如果snapcontainer尚未在doc待处理的scrollsnapchange事件目标中:

      1. snapcontainer附加到doc待处理的scrollsnapchange事件目标

注意: 当在滚动器上发生快照时(无论是由于布局变化还是滚动操作),与该滚动器关联的scrollsnapchanging block-axis targetscrollsnapchanging inline-axis target会被更新,并表示该滚动器的当前快照目标。这允许更新scrollsnapchange目标算法使用这些快照目标来确定是否应该触发scrollsnapchange事件。

当被要求为调度待处理的scrollsnapchange事件时,对于Documentdoc,执行以下步骤:

  1. 对于doc待处理的scrollsnapchange事件目标中的每个项target

    1. 初始时让blockTargetinlineTarget都为null。

    2. 如果与target关联的scrollsnapchangeTargetBlock是一个伪元素,则将blockTarget设置为该scrollsnapchangeTargetBlock的拥有元素。

    3. 否则,将blockTarget设置为该scrollsnapchangeTargetBlock

    4. 如果与target关联的scrollsnapchangeTargetInline是一个伪元素,则将inlineTarget设置为该scrollsnapchangeTargetInline的拥有元素。

    5. 否则,将inlineTarget设置为该scrollsnapchangeTargetInline

    6. 触发一个SnapEventsnapevent,命名为scrollsnapchange,在target上触发,并让snapeventsnapTargetBlocksnapTargetInline属性分别为blockTargetinlineTarget

  2. 清空doc待处理的scrollsnapchange事件目标

5.1.3. scrollsnapchanging

scrollsnapchanging 被调度:

滚动操作可能朝着特定位置进行动画(例如,滚动条箭头点击、箭头键按压、"behavior: smooth" 的程序性滚动)或可能直接跟踪用户的输入(例如,触控滚动、滚动条拖动)。在这两种情况下,用户代理选择在每个轴上的最终快照目标,以便在滚动操作达到其预期滚动位置后,滚动器将要快照

scrollsnapchanging旨在尽早通知网页,滚动操作将导致快照容器所快照的快照目标发生变化。用户代理应根据滚动器将要快照最终快照目标来评估是否触发scrollsnapchanging事件,假如滚动操作达到其预期滚动位置。

注意:由于scrollsnapchanging给网页提供了关于未来快照的提示,因此通过scrollsnapchanging事件提示的快照可能不会实现,因为后续的滚动输入可能进一步改变快照容器的滚动位置,导致不同的最终快照目标。

scrollsnapchanging事件在scroll事件之前触发。

每个Document都有一个相关的待处理scrollsnapchanging事件目标列表,初始为空。

每个快照容器在块轴和行内轴上各有一个scrollsnapchanging block-axis target和一个scrollsnapchanging inline-axis target,每个都可以为null(如果容器在该轴上不进行快照),或者是该容器所快照的快照目标

当被要求为快照容器snapcontainer,更新scrollsnapchanging目标时,给定一个快照目标 newBlockTarget和一个快照目标 newInlineTarget,执行以下步骤:

  1. docsnapcontainer的相关Document

  2. blockTarget为与snapcontainer关联的scrollsnapchanging block-axis target

  3. inlineTarget为与snapcontainer关联的scrollsnapchanging inline-axis target

  4. 如果newBlockTargetblockTarget不是同一个快照目标

    1. 将与snapcontainer关联的scrollsnapchanging block-axis target设置为newBlockTarget

    2. 如果snapcontainer尚未在docpending scrollsnapchanging event targets中,

      1. snapcontainer附加到docpending scrollsnapchanging event targets

  5. 如果newInlineTargetinlineTarget不是同一个快照目标

    1. 将与snapcontainer关联的scrollsnapchanging inline-axis target设置为newInlineTarget

    2. 如果snapcontainer尚未在docpending scrollsnapchanging event targets中,

      1. snapcontainer附加到docpending scrollsnapchanging event targets

当要求为一个Document doc 分派待处理的scrollsnapchanging事件时,执行以下步骤:

  1. 对于doc待处理的scrollsnapchanging事件目标中的每个项target

    1. 最初将blockTargetinlineTarget设为null。

    2. 如果与target关联的scrollsnapchanging块轴目标是一个伪元素,将blockTarget设置为该scrollsnapchanging块轴目标的拥有元素。

    3. 否则,将blockTarget设置为该scrollsnapchanging块轴目标

    4. 如果与target关联的scrollsnapchanging行内轴目标是一个伪元素,将inlineTarget设置为该scrollsnapchanging行内轴目标的拥有元素。

    5. 否则,将inlineTarget设置为该scrollsnapchanging行内轴目标

    6. target上触发一个名为scrollsnapchangingSnapEvent,让snapeventsnapTargetBlocksnapTargetInline属性分别为blockTargetinlineTarget

  2. 清空doc待处理的scrollsnapchanging事件目标

5.1.4.因布局变化而触发的 Snap 事件

当一个snap container snapcontainer 重新对齐时,执行以下步骤:
  1. newBlockTargetsnapcontainer在块轴上已对齐到的 snap 目标,如果未对齐到任何元素,则为 null。

  2. newInlineTargetsnapcontainer在行内轴上已对齐到的 snap 目标,如果未对齐到任何元素或伪元素,则为 null。

  3. 使用newBlockTarget作为 newBlockTarget 和newInlineTarget作为 newInlineTarget,执行更新 scrollsnapchanging 目标的步骤。

  4. snapcontainer执行更新 scrollsnapchange 目标的步骤。

5.2. SnapEvent 接口

dictionary SnapEventInit : EventInit {
  Node? snapTargetBlock;
  Node? snapTargetInline;
};

[Exposed=Window]
interface SnapEvent : Event {
  constructor(DOMString type, optional SnapEventInit eventInitDict = {});
  readonly attribute Node? snapTargetBlock;
  readonly attribute Node? snapTargetInline;
};
snapTargetBlock, 类型为 Node,只读,允许为 null

在与该 snap 事件相关联的snap 位置上,snap 容器在块轴上对齐到的元素。如果与此对应的snap 目标是伪元素,则这是该伪元素的拥有元素。

snapTargetInline, 类型为 Node,只读,允许为 null

在与该 snap 事件相关联的snap 位置上,snap 容器在行内轴上对齐到的元素。如果与此对应的snap 目标是伪元素,则这是该伪元素的拥有元素。

对于scrollsnapchange事件,snap 位置是 snap 容器在 scroll snap 之后已实现的位置。对于scrollsnapchanging事件,snap 位置是 scroll 操作结束后 snap 容器将最终对齐的位置。

SnapEvent 不会冒泡,除非事件目标是DocumentSnapEvent不可取消。

附录 A:事件处理程序

本节应移至 HTML 事件处理程序规范

元素、文档对象和窗口对象上的事件处理程序

以下是所有事件处理程序(及其相应的事件处理程序事件类型),这些必须被所有HTML 元素支持,作为事件处理程序内容属性事件处理程序 IDL 属性;并且所有Window对象和Document对象也必须支持这些作为事件处理程序 IDL 属性

事件处理程序 事件处理程序事件类型
onscrollsnapchange scrollsnapchange
onscrollsnapchanging scrollsnapchanging

GlobalEventHandlers 接口混合的扩展

本规范扩展了来自 HTML 的 GlobalEventHandlers 接口混合,为 SnapEvent 添加 事件处理 IDL 属性,如元素、文档对象和窗口对象上的事件处理程序中所定义。

IDL 定义

partial interface mixin GlobalEventHandlers {
  attribute EventHandler onsnapchanged;
  attribute EventHandler onsnapchanging;
};

6. 隐私考虑

本规范中的功能没有已知的隐私影响。

7. 安全考虑

本规范中的功能没有已知的安全影响。

符合性

文档约定

符合性要求通过描述性断言和 RFC 2119 术语的组合进行表达。本文件规范部分中的关键字“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按 RFC 2119 的描述进行解释。然而,为了可读性,这些词在本规范中并不全部以大写字母形式出现。

本规范的所有文本都是规范性的,除了明确标记为非规范性的部分、示例和注释。[RFC2119]

本规范中的示例以“例如”引入,或通过 class="example" 与规范文本区分开,像这样:

这是一个信息性示例。

信息性注释以“注释”一词开头,并通过 class="note" 与规范文本区分开,像这样:

注:这是一个信息性注释。

建议是规范性部分,样式设计为引起特别注意,并通过 <strong class="advisement"> 与其他规范文本区分开,像这样:用户代理必须提供可访问的替代方案。

符合性类别

本规范的符合性定义了三种符合性类别:

样式表
一个CSS 样式表
渲染器
一个用户代理,解释样式表的语义并渲染使用这些样式表的文档。
创作工具
一个用户代理,编写样式表。

一个样式表符合本规范,如果它所使用的语法符合本模块中定义的通用 CSS 语法和每个特性定义的单独语法。

一个渲染器符合本规范,如果除了按照适当规范解释样式表外,它还通过正确解析本规范定义的所有特性并相应地渲染文档。然而,用户代理由于设备的限制无法正确渲染文档并不意味着该用户代理不符合规范。(例如,用户代理不需要在单色显示器上渲染颜色。)

一个创作工具符合本规范,如果它编写的样式表在通用 CSS 语法和本模块中每个特性的单独语法上都是语法正确的,并且满足本模块中描述的样式表的所有其他符合性要求。

部分实现

为了使作者能够利用向前兼容的解析规则来指定后备值,CSS 渲染器必须将任何没有可用支持级别的 at-rules、属性、属性值、关键字和其他语法构造视为无效(并根据需要忽略)。特别是,用户代理不得选择性地忽略不支持的组件值,并在单个多值属性声明中尊重受支持的值:如果任何值被视为无效(如不支持的值必须是),CSS 要求整个声明被忽略。

不稳定和专有特性的实现

为了避免与未来稳定的 CSS 特性发生冲突,CSS 工作组建议遵循最佳实践,以实现不稳定特性和专有扩展的 CSS。

非实验性实现

一旦规范达到候选推荐阶段,非实验性实现就成为可能,实施者应发布任何能够证明按照规范正确实现的 CR 级特性的无前缀实现。

为了建立和维护 CSS 在实现之间的互操作性,CSS 工作组请求非实验性 CSS 渲染器在发布任何 CSS 特性的无前缀实现之前,向 W3C 提交实施报告(如有必要,还需提交用于该实施报告的测试用例)。提交给 W3C 的测试用例将由 CSS 工作组审查和修正。

有关提交测试用例和实施报告的更多信息,请访问 CSS 工作组网站:https://www.w3.org/Style/CSS/Test/。如有问题,请联系public-css-testsuite@w3.org 邮件列表。

索引

本规范定义的术语

通过引用定义的术语

参考文献

规范性参考文献

[CSS-ALIGN-3]
Elika Etemad; Tab Atkins Jr.. CSS 盒子对齐模块 第 3 级。2023年2月17日。WD。网址:https://www.w3.org/TR/css-align-3/
[CSS-DISPLAY-3]
Elika Etemad; Tab Atkins Jr.. CSS 显示模块 第 3 级。2023年3月30日。CR。网址:https://www.w3.org/TR/css-display-3/
[CSS-OVERFLOW-3]
Elika Etemad; Florian Rivoal. CSS 溢出模块 第 3 级。2023年3月29日。WD。网址:https://www.w3.org/TR/css-overflow-3/
[CSS-SCROLL-SNAP-1]
Matt Rakow; 等. CSS 滚动捕捉模块 第 1 级。2021年3月11日。CR。网址:https://www.w3.org/TR/css-scroll-snap-1/
[CSS-VALUES-4]
Tab Atkins Jr.; Elika Etemad. CSS 值和单位模块 第 4 级。2024年3月12日。WD。网址:https://www.w3.org/TR/css-values-4/
[CSSOM-VIEW-1]
Simon Pieters. CSSOM 视图模块。2016年3月17日。WD。网址:https://www.w3.org/TR/cssom-view-1/
[DOM]
Anne van Kesteren. DOM 标准。现行标准。网址:https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 等. HTML 标准。现行标准。网址:https://html.spec.whatwg.org/multipage/
[RFC2119]
S. Bradner. RFC 中用于指示要求级别的关键字。1997年3月。最佳当前实践。网址:https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL 标准。现行标准。网址:https://webidl.spec.whatwg.org/

属性索引

名称 初始 适用对象 继承 百分比 动画类型 规范顺序 计算值
scroll-start-target [ none | auto ] none 所有元素 N/A 按语法 见各个属性

IDL 索引

dictionary SnapEventInit : EventInit {
  Node? snapTargetBlock;
  Node? snapTargetInline;
};

[Exposed=Window]
interface SnapEvent : Event {
  constructor(DOMString type, optional SnapEventInit eventInitDict = {});
  readonly attribute Node? snapTargetBlock;
  readonly attribute Node? snapTargetInline;
};

partial interface mixin GlobalEventHandlers {
  attribute EventHandler onsnapchanged;
  attribute EventHandler onsnapchanging;
};

问题索引

':snapped' 伪类将被舍弃,取而代之的是一种容器状态查询方法。
本节应移动到 HTML 事件处理程序规范中。