CSS 自定义高亮 API 模块第 1 级

W3C 工作草案,

更多关于此文档的详情
此版本:
https://www.w3.org/TR/2021/WD-css-highlight-api-1-20211215/
最新发布版本:
https://www.w3.org/TR/css-highlight-api-1/
编辑草案:
https://drafts.csswg.org/css-highlight-api-1/
以前的版本:
历史:
https://www.w3.org/standards/history/css-highlight-api-1
反馈:
CSSWG 问题库
内嵌在规格中的问题
编辑者:
Florian Rivoal (代表 Bloomberg)
Sanket Joshi (微软公司)
Megan Gardner (苹果公司)
为此规范建议编辑:
GitHub 编辑器

摘要

此 CSS 模块描述了一种机制,用于对通过脚本标识的文档中的任意范围进行样式设置。

CSS 是一种用于描述结构化文档(如 HTML 和 XML)的渲染语言,适用于屏幕、纸张等介质。

本文档的状态

此部分描述了本文档在发布时的状态。 当前 W3C 出版物列表和该技术报告的最新修订版可以在 W3C 技术报告索引中找到。

本文档由 CSS 工作组 以 工作草案 的形式发布, 使用 推荐轨道。 作为工作草案发布并不意味着 W3C 及其成员的认可。

这是一个草稿文档,可能随时更新、替换或被其他文档取代。作为工作进展,不应将此文档作为参考文献。

请通过 在 GitHub 上提交问题(首选)来发送反馈, 标题中包括规范代码 “css-highlight-api”,例如: “[css-highlight-api] …评论摘要…”。 所有问题和评论都已 归档。 或者,反馈可以发送到 (已归档) 公共邮件列表 www-style@w3.org。

本文档受 2021 年 11 月 2 日的 W3C 过程文档 约束。

本文档由根据 W3C 专利政策 运作的小组编写。 W3C 维护了一个 与该小组交付物相关的公开专利披露列表; 该页面还包括披露专利的说明。 个人如果实际知晓专利且认为该专利包含 必要声明,必须根据 W3C 专利政策第 6 节 进行披露。

1. 简介

本节为非规范性内容。

自定义高亮 API 扩展了 高亮伪元素 的概念(参见 CSS 伪元素 4 § 3 高亮伪元素), 通过为 web 开发者提供一种方式来设置任意 Range 对象的文本样式, 而不仅限于用户代理定义的 ::selection、::inactive-selection、 ::spelling-error, 和 ::grammar-error。 这在多种场景中非常有用, 包括希望实现自定义选择的编辑框架、 在虚拟化文档中实现页面查找、 表示在线协作的多重选择, 或拼写检查框架。

自定义高亮 API 提供了一种编程方式,用于添加和移除不影响底层 DOM 结构的高亮, 而是基于 range 对象来应用样式, 通过 ::highlight() 伪元素进行访问。

以下代码使用 ::highlight() 伪元素 为文本 One two 应用了黄色背景和蓝色前景色。 它通过将一个 Highlight 添加到 HighlightRegistry 中来实现(这两个都是此规范引入的新概念)。 该 Highlight 将包含一个 Range ,其边界点围绕着文本 One two。
<style>
  :root::highlight(example-highlight) {
    background-color: yellow;
    color: blue;
  }
</style>
<body><span>One </span><span>two </span><span>three…</span>
<script>
  let r = new Range();
  r.setStart(document.body, 0);
  r.setEnd(document.body, 2);

  CSS.highlights.set("example-highlight", new Highlight(r));
</script>

结果如下所示:

One Two three…

2. 模块交互

本模块依赖于 Infra 标准 [INFRA] 和 WebIDL [WebIDL]。

它假定读者总体熟悉 CSS 和 DOM 标准 [DOM], 并专门扩展了 CSS 伪元素模块第 4 级 [css-pseudo-4] 中定义的机制,以处理高亮伪元素。 选择器第 4 级 [selectors-4] 规范 定义了伪元素通常如何工作。

有关完整的依赖项列表,请参见参考资料。

注: 此草案是一个早期版本。 随着其逐渐成熟,CSS-WG 可以决定将其保留为独立模块, 或者可能倾向于将其并入 [css-pseudo-4], 或该模块的后续版本。

3. 设置自定义高亮

3.1. 创建自定义高亮

自定义高亮是 表示文档各部分的一组范围。 它们不一定符合元素树的结构, 并且可以任意跨越元素边界,而无需遵循其嵌套结构。 它们可用于影响文档这些部分的外观 (参见§ 4 自定义高亮的样式), 或处理与它们相关联的事件 (参见§ 6 事件处理)。

自定义高亮由Highlight对象表示,它们是类集合对象,其集合条目是 AbstractRange 对象。范围可以通过将它们传递给构造函数, 或使用类集合对象的常规 API 操作其集合条目,将其添加到自定义 高亮中。

注: 由于自定义高亮中的范围是 AbstractRange 对象, 作者可以选择使用 Range 对象或 StaticRange 对象。 有关此选择及其影响的更多详细信息, 请参见§ 5.2 范围更新和失效。

enum HighlightType {
  "highlight",
  "spelling-error",
  "grammar-error"
};

[Exposed=Window]
interface Highlight {
  constructor(AbstractRange... initialRanges);
  setlike<AbstractRange>;
  attribute long priority;
  attribute HighlightType type;
};

有关 priority 属性的更多信息,请参见§ 4.2.5 重叠高亮的优先级。

有关 type 属性的更多信息,请参见§ 4.2.6 高亮类型。

当调用Highlight(AbstractRange... initialRanges)构造函数时, 运行以下步骤:
  1. 令 highlight 为新的 Highlight 对象。
  2. 将 highlight 的 priority 设置为 0。
  3. 将 highlight 的 type 设置为 highlight。
  4. 对 initialRanges 中的每个 range, 令 rangeArg 为将 range 转换为 ECMAScript 值的结果, 然后运行内置类集合 add 函数的步骤, 其中 highlight 作为 this 值, rangeArg 作为参数。
  5. 返回 highlight。

3.2. 注册自定义高亮

为了产生效果,自定义高亮需要被注册到高亮注册表中。

高亮 注册表通过 highlights 属性访问,该属性位于 CSS 命名空间中, 并表示为当前全局对象的关联 Document 注册的所有自定义高亮。 它是一个 maplike,并且可以使用常规方法进行更新。 它的映射条目最初为空。

如果一个自定义高亮 位于高亮 注册表中,则称其已被注册。 如果之后将其移除,则它不再处于已注册状态。

partial namespace CSS {
  readonly attribute HighlightRegistry highlights;
};

[Exposed=Window]
interface HighlightRegistry {
  maplike<DOMString, Highlight>;
};
要注册自定义高亮, 调用高亮注册表的 set 方法,这将运行内置类似映射设置函数的步骤, 将上下文对象作为 this 值, 传入的自定义高亮名称作为 keyArg, 传入的高亮作为 valueArg。

当自定义高亮被注册时分配的自定义高亮名称用于在样式设置时标识高亮(参见 § 4 自定义高亮的样式设置)。

注意:在注册自定义高亮时, 建议作者使用有效的 CSS 标识符作为自定义高亮名称。 使用无效标识符可能会使高亮难以甚至无法通过 CSS 设置样式。

注:可以使用多个自定义高亮名称来注册一个自定义高亮。 但是,使用多个名称来设置高亮的样式 会为该高亮分配多组不同的样式, 而在绘制期间,对于这些样式组中相互冲突的样式, 无法控制其堆叠顺序。 这可能会限制作者的使用,并可能导致令人困惑的绘制行为 (有关更多上下文,请参见下面的示例)。 因此,建议作者在设置样式时,每个高亮仅使用一个名称。

<style>
  div::highlight(bar) {
    color: red;
  }
  div::highlight(foo) {
    color: green;
  }
</style>
<body><div>abc</div>
<script>
  let div = document.body.firstChild;
  let r = new Range();
  r.setStart(div, 0);
  r.setEnd(div, 1);
  let h = new Highlight(r);
  CSS.highlights.set('foo', h);
  CSS.highlights.set('bar', h);
</script>

在上面的示例中, 同一个自定义高亮对象被以 foo 和 bar 两个名称注册。 由于每个样式规则都针对相同的高亮且具有相同的优先级, 作者可能期望最后一个规则在级联顺序中获胜, 并且高亮内容显示为绿色。 然而,每个高亮名称都将获得独立的一组高亮样式, 并且高亮会根据名称绘制多次。 在这种情况下,因为 foo 在 bar 之前注册, 高亮将首先用 foo 的颜色(绿色)绘制, 然后用 bar 的颜色(红色)绘制。 因此,高亮的内容将显示为红色。

4. 自定义高亮的样式设置

4.1. 自定义高亮伪元素: ::highlight()

::highlight(<custom-highlight-name>) 伪元素 (也称为自定义高亮伪元素) 表示文档中 包含或部分包含在 所有范围中的已注册的自定义高亮部分,并且使用了自定义高亮名称<custom-highlight-name>,如果存在的话。 <custom-highlight-name>必须是有效的 CSS <ident-token>。

4.2. 处理模型

4.2.1. 适用属性

自定义高亮伪元素, 类似于内置的高亮伪元素, 只能使用有限的属性集进行样式设置。 有关完整列表,请参见 CSS 伪元素 4 § 3.2 高亮的样式设置。

4.2.2. 默认样式

UA 不能在默认 UA 样式表中为自定义高亮伪元素定义任何样式。 自定义高亮伪元素继承其起源元素的样式。

4.2.3. 级联与继承

级联和继承的 自定义高亮伪元素与内置的高亮伪元素的处理方式相同, 如 CSS 伪元素 4 § 3.5 级联与每个元素的高亮样式 中定义。

4.2.4. 绘制

自定义高亮的绘制方式与内置的高亮伪元素的绘制方式相同, 如CSS 伪元素 4 § 3.4 高亮区域和CSS 伪元素 4 § 3.6 高亮的绘制中指定的, 但有以下说明:

4.2.5. 重叠高亮的优先级

自定义高亮的 priority 属性 定义其优先级。 该属性用于确定对应的高亮覆盖在绘制操作期间的堆叠顺序(参见 § 4.2.4 绘制)。 更高的优先级意味着在堆叠顺序中更高。 如果未显式设置其priority 属性,自定义高亮的默认数值优先级为 0。

当两个或多个自定义高亮具有相同的数值优先级时, 最后注册的高亮具有更高的有效优先级。

<style>
  :root::highlight(foo) {
    color:blue;
    background-color:yellow;
  }
  :root::highlight(bar) {
    background-color:orange;
  }
</style>
<body>Some text
<script>
  let textNode = document.body.firstChild;

  let r1 = new Range();
  r1.setStart(textNode, 0);
  r1.setEnd(textNode, 6);

  let r2 = new Range();
  r2.setStart(textNode, 3);
  r2.setEnd(textNode, 9);

  let h1 = new Highlight(r1);
  let h2 = new Highlight(r2);

  CSS.highlights.set("foo", h1);
  CSS.highlights.set("bar", h2);
</script>

由于没有设置优先级 (即 h1 和 h2 之间没有优先级差异), 自定义高亮的样式将按照插入到高亮注册表的顺序进行堆叠。 渲染结果将为 "Som" 显示为蓝色文本,黄色背景, "e t" 显示为蓝色文本,橙色背景, "ext" 显示为默认文本颜色,橙色背景。

Some text

将 h1.priority = 1; 设置为1将使 h1 的堆叠顺序高于 h2, 结果为 "Some t" 显示为蓝色文本,黄色背景, "ext" 显示为默认文本颜色,橙色背景。

Some text

4.2.6. 高亮类型

自定义高亮的 type 属性由作者用于 指定高亮的语义含义。 这使辅助技术能够在向用户呈现高亮时 包含此含义。

如果自定义高亮的 type 属性尚未被显式设置,则其默认类型为 highlight。

注:建议作者在使用自定义高亮来强调 拼写错误的内容时,将其 type 设置为 spelling-error。 建议作者在使用该 自定义高亮来强调 拼写错误的内容时。 建议作者将 自定义高亮的 type 设置为 grammar-error, 当该 自定义高亮用于强调 语法不正确的内容时。 对于所有其他使用场景,最好将 type 保持为 highlight。

用户代理应使自定义 高亮可供辅助技术使用。 当使用给定的平台无障碍 API 呈现高亮时, 用户代理应按照其 type 属性所指定的内容, 以该无障碍 API 所能支持的最高具体程度来呈现高亮的语义含义。

注:例如, 如果某个平台无障碍 API 能够明确表达拼写错误和语法错误, 那么预期用户代理使用这些能力来传达 spelling-error 和 spelling-error 高亮的语义。 如果某个无障碍 API 仅具备表达拼写错误的能力, 那么预期用户代理会使用拼写错误语义来传达 spelling-error 和 grammar-error 这两种高亮。 如果某个无障碍 API 既不支持表达拼写错误,也不支持表达语法错误, 那么无论其实际 type 如何,用户代理都会将所有高亮呈现为通用的 highlight。

注:之所以选择这一初始类型集合, 是因为预计它们会成为 Highlight API 的常见使用场景, 并且当前的平台无障碍 API 已在一定程度上支持表达它们的语义。 目前,无障碍 API 尚无法表达 其他预期 Highlight API 使用场景的具体语义。 随着无障碍 API 获得对表达 Highlight API 其他常见使用场景的支持, 以后可以向 HighlightType 添加更多类型。

5. 响应更改

5.1. 重绘

在高亮注册表中添加或移除 自定义 高亮, 或者在已注册的自定义 高亮中添加或移除范围, 必须使用户代理重新评估渲染, 并在适当时重新绘制。

用户代理还必须根据作者对 priority 的更改, 或对已注册的自定义高亮中 Range 的边界点的更改, 根据需要重新绘制高亮。

我们应该如何规定 此重新评估的时机(以及同步性)?[议题 #4596]

5.2. 范围更新和失效

作者可以使用 Range 或 StaticRange 构建自定义 高亮。

生成的自定义 高亮表示文档中的相同部分, 并且可以采用相同的样式。 但是,当底层文档被修改时, 其行为会有所不同。

Range 是实时范围。 用户代理会响应与范围重叠或发生在其边界处的 DOM 更改,调整 Range 的边界 点, 并相应地重新绘制。实时范围的边界 点也可以由作者更改。

另一方面, 用户代理不得响应 DOM 更改而调整 StaticRange 的边界 点, 作者在创建后也不能修改它们。 用户代理应存储实际的 StaticRange, 而不是在其背后使用实时 Range。

随着 DOM 被修改而更新所有 Range 对象会产生显著的性能开销。 强烈建议那些打算观察 DOM 更改并通过 调整或重新创建其自定义高亮中的范围来响应这些更改的作者使用 StaticRange, 以避免这一代价高昂但不必要的步骤。

反过来,使用 StaticRange 的作者 应观察并响应 DOM 更改, 丢弃过时的范围或自定义高亮,并重新创建新的。

在计算如何呈现文档时, 如果与该文档窗口关联的高亮注册表中的任何范围的起始节点或结束 节点引用了一个 Node, 且其包含影子树的根不是该文档, 则用户代理必须忽略该范围。 如果与该文档窗口关联的高亮注册表中的任何 StaticRange 无效, 则用户代理必须忽略该范围。

自定义 高亮中的 StaticRange 与 [css-contain-2] 的交互似乎 存在问题: 对于一个完全遏制的元素, 预期对该元素后代所做的 DOM 更改 不会导致该被遏制元素之外的元素 失效并重新计算样式/重新绘制。 但是,如果某个静态范围的一个边界点位于被遏制的子树内部, 而另一个边界点位于其外部, 并且被遏制子树中的 DOM 被更改, 使得内部的边界点不再指向有效节点, 则整个范围都应被忽略, 这会影响被遏制子树之外的绘制。 这是样式遏制的缺陷, 还是上述失效逻辑的缺陷, 或者是其他问题?[议题 #4598]

6. 事件处理

事件部分待定, 基于 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/highlight/events-explainer.md

自定义高亮是否应有专门的事件处理机制, 还是应该将其添加到伪元素的通用处理机制中?

附录 A. 隐私和安全考虑

本节为非规范性内容。

本规范认为不会引入任何新的安全或隐私问题。 如果有人认为此结论不准确,欢迎联系 CSS 工作组或联合编辑人员。

附录 B. 致谢

本节为非规范性内容。

致谢需要感谢的人员(编辑之外)。

附录 C. 变更记录

本节为非规范性内容。

自2020年12月8日工作草案以来的变更

除了各种编辑改进和小的调整,主要更改包括:

  • 将 HighlightsRegister 重命名为 HighlightRegistry

  • 从 HighlightRegistry 中移除了冗余的 add() 方法。 (参见 议题 6092)

  • 使自定义高亮覆盖层堆叠在原生高亮覆盖层之下。 (参见 议题 4595)

  • 使用整数而不是浮点数处理高亮优先级。 (参见 议题 4592)

  • 将高亮优先级的默认值定义为 0。 (参见 议题 6136)

  • 使 HighlightRegistry 成为 maplike(而不是 setlike) 并从 Highlight 中移除 name 属性。 (参见 议题 5910)

  • 澄清了来自错误窗口的范围不会被绘制。 (参见 议题 6417)

  • 规定自定义高亮没有用户代理样式。 (参见 议题 6375)

  • 将范围失效的定义交由 [DOM] 规范处理 范围失效 (参见 议题 4597)

  • 向 type 属性添加到了 Highlight 中,以便为不同的高亮提供更清晰的语义, 从而支持将高亮呈现给无障碍工具。 (参见 议题 6498)

自2020年10月22日工作草案以来的变更

自2020年10月22日工作草案以来仅进行了编辑修改; 见差异。

合规性

文档约定

合规性要求通过描述性断言和 RFC 2119 术语的结合来表达。关键字“MUST”(必须)、“MUST NOT”(不得)、“REQUIRED”(必需)、 “SHALL”(应)、“SHALL NOT”(不应)、“SHOULD”(应该)、“SHOULD NOT”(不应该)、“RECOMMENDED”(推荐)、“MAY”(可以)、 和“OPTIONAL”(可选)在本文件的规范部分中,应按照 RFC 2119 中的描述进行解释。 然而,为了提高可读性,这些词在本规范中并没有全部以大写字母出现。

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

本规范中的示例以“例如”开头,或使用 class="example" 与规范性文本分开显示,如下所示:

这是一个信息性示例。

信息性注释以“Note”开头,并使用 class="note" 与规范性文本分开显示,如下所示:

注意,这是一个信息性注释。

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

合规性类别

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

样式表
一份CSS 样式表。
渲染器
一个解释样式表语义并渲染使用它们的文档的用户代理(UA)。
作者工具
一个编写样式表的用户代理(UA)。

如果样式表中使用本模块定义的语法的所有语句都根据通用 CSS 语法和本模块中定义的各个功能的语法是有效的,则该样式表符合本规范。

如果渲染器除了按照适当规范解释样式表外,还正确解析并支持本规范中定义的所有功能并相应地渲染文档,则该渲染器符合本规范。然而,由于设备的限制导致用户代理无法正确渲染文档,并不会使用户代理不符合规定。(例如,用户代理不需要在单色显示器上渲染颜色。)

如果作者工具编写的样式表根据通用 CSS 语法和本模块中定义的各个功能的语法是语法正确的,并且符合本模块所描述的样式表的所有其他合规性要求,则该作者工具符合本规范。

部分实现

为了让作者能够利用向前兼容的解析规则来分配回退值,CSS 渲染器必须将无法使用的级别支持的 at-rule、属性、属性值、关键字和其他语法结构视为无效(并根据需要忽略)。特别是,用户代理不得在单个多值属性声明中选择性地忽略不支持的值并接受支持的值:如果任何值被视为无效(因为不支持的值必须被视为无效),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-CASCADE-5]
Elika Etemad; Miriam Suzanne; Tab Atkins Jr.. CSS 级联和继承 第5版. 2021年12月3日. 工作草案. URL: https://www.w3.org/TR/css-cascade-5/
[CSS-CONTAIN-2]
Tab Atkins Jr.; Florian Rivoal; Vladimir Levin. CSS 包含模块 第2版. 2020年12月16日. 工作草案. URL: https://www.w3.org/TR/css-contain-2/
[CSS-PSEUDO-4]
Daniel Glazman; Elika Etemad; Alan Stearns. CSS 伪元素模块 第4版. 2020年12月31日. 工作草案. URL: https://www.w3.org/TR/css-pseudo-4/
[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS 语法模块 第3版. 2019年7月16日. 候选推荐标准. URL: https://www.w3.org/TR/css-syntax-3/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS 对象模型(CSSOM). 2021年8月26日. 工作草案. URL: https://www.w3.org/TR/cssom-1/
[DOM]
Anne van Kesteren. DOM 标准. 现行标准. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML 标准. 现行标准. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra 标准. 现行标准. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. 用于RFC中的关键字以表示需求级别. 1997年3月. 当前最佳实践. URL: https://datatracker.ietf.org/doc/html/rfc2119
[SELECTORS-4]
Elika Etemad; Tab Atkins Jr.. 选择器 第4版. 2018年11月21日. 工作草案. URL: https://www.w3.org/TR/selectors-4/
[WebIDL]
Edgar Chen; Timothy Gu. Web IDL 标准. 现行标准. URL: https://webidl.spec.whatwg.org/

IDL 索引

enum HighlightType {
  "highlight",
  "spelling-error",
  "grammar-error"
};

[Exposed=Window]
interface Highlight {
  constructor(AbstractRange... initialRanges);
  setlike<AbstractRange>;
  attribute long priority;
  attribute HighlightType type;
};

partial namespace CSS {
  readonly attribute HighlightRegistry highlights;
};

[Exposed=Window]
interface HighlightRegistry {
  maplike<DOMString, Highlight>;
};

问题索引

我们应该如何指定此重新评估的时间(以及同步性)? [问题 #4596] ↵
StaticRange 在 自定义高亮 中与 [css-contain-2] 的交互似乎有问题: 在完全包含的元素上,你应该预期对该元素后代的 DOM 更改不会导致外部元素的失效和重新样式/重绘。 然而,如果静态范围有一个边界点在包含的子树内,另一个边界点在它之外,并且子树内的 DOM 更改使得内部边界点不再指向有效节点,则整个范围应被忽略,这将影响包含子树外部的绘制。 这是 样式包含 的弱点,还是上述失效逻辑的问题,或是其他原因? [问题 #4598] ↵
关于事件的部分待定,基于 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/highlight/events-explainer.md ↵
自定义高亮是否应该有一个专用的事件处理机制,还是应该将其添加到伪元素中? ↵
认可那些(除了编辑以外)应得功劳的人。 ↵