1. 简介
本节为非规范性内容。
自定义高亮 API 扩展了 高亮伪元素 的概念(参见 CSS 伪元素 4 § 3 高亮伪元素), 通过为 web 开发者提供一种方式来设置任意 Range 对象的文本样式, 而不仅限于用户代理定义的 ::selection、::inactive-selection、 ::spelling-error, 和 ::grammar-error。 这在多种场景中非常有用, 包括希望实现自定义选择的编辑框架、 在虚拟化文档中实现页面查找、 表示在线协作的多重选择, 或拼写检查框架。
自定义高亮 API 提供了一种编程方式,用于添加和移除不影响底层 DOM 结构的高亮, 而是基于 range 对象来应用样式, 通过 ::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 >
结果如下所示:
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)构造函数时,
运行以下步骤:
- 令 highlight 为新的
Highlight对象。 - 将 highlight 的
priority设置为0。 - 将 highlight 的
type设置为highlight。 - 对
initialRanges中的每个 range, 令 rangeArg 为将 range 转换为 ECMAScript 值的结果, 然后运行内置类集合 add 函数的步骤, 其中 highlight 作为this值, rangeArg 作为参数。 - 返回 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 高亮的绘制中指定的, 但有以下说明:
- 折叠的范围不会被渲染。
-
同一范围中的重叠自定义高亮
将作为一个表示这些重叠部分的并集的单一范围进行渲染。
以下示例中,半透明的蓝色背景只会渲染为一个单一高亮区域, 而不会出现两个重叠并彼此可见的高亮。
< style > :: highlight ( sample ) { background-color : rgba( 0 , 0 , 255 , 0.3 ); } </ style > < body > Lorem Ipsum.< script > let textNode= document. body. firstChild; let r1= new Range(); r1. setStart( textNode, 1 ); r1. setEnd( textNode, 5 ); let r2= new Range(); r2. setStart( textNode, 3 ); r2. setEnd( textNode, 7 ); CSS. highlights. set( "sample" , new Highlight( r1, r2)); </ script > 换句话说,这种渲染是正确的:
Lorem Ipsum.然而,这种渲染是不正确的:
Lorem Ipsum. - 高亮覆盖的自定义高亮 在堆叠顺序中位于内置的高亮伪元素之下, 如CSS 伪元素 4 § 3.6 高亮的绘制中描述的顺序。
- 多个高亮覆盖的自定义高亮 的相对堆叠顺序由它们的优先级定义(参见§ 4.2.5 重叠高亮的优先级)。
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" 显示为默认文本颜色,橙色背景。
将 h1
设置为1将使 h1 的堆叠顺序高于 h2,
结果为 "Some t" 显示为蓝色文本,黄色背景,
"ext" 显示为默认文本颜色,橙色背景。
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。
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
自定义高亮是否应有专门的事件处理机制, 还是应该将其添加到伪元素的通用处理机制中?
附录 C. 变更记录
本节为非规范性内容。
自2020年12月8日工作草案以来的变更
除了各种编辑改进和小的调整,主要更改包括:
-
将
HighlightsRegister重命名为HighlightRegistry -
从
HighlightRegistry中移除了冗余的add()方法。 (参见 议题 6092) -
使自定义高亮覆盖层堆叠在原生高亮覆盖层之下。 (参见 议题 4595)
-
使用整数而不是浮点数处理高亮优先级。 (参见 议题 4592)
-
将高亮优先级的默认值定义为 0。 (参见 议题 6136)
-
使 HighlightRegistry 成为 maplike(而不是 setlike) 并从 Highlight 中移除
name属性。 (参见 议题 5910) -
澄清了来自错误窗口的范围不会被绘制。 (参见 议题 6417)
-
规定自定义高亮没有用户代理样式。 (参见 议题 6375)
-
向
type属性添加到了Highlight中,以便为不同的高亮提供更清晰的语义, 从而支持将高亮呈现给无障碍工具。 (参见 议题 6498)
自2020年10月22日工作草案以来的变更
自2020年10月22日工作草案以来仅进行了编辑修改; 见差异。