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
。 - 对于每个 range 的
initialRanges
, 让 rangeArg 是 将 range 转换为 ECMAScript 值的结果, 然后运行 内置类似集合的添加功能步骤, 以 highlight 作为this
值, 并以 rangeArg 作为参数。 - 返回 highlight。
3.2. 注册自定义高亮
高亮注册表可通过 highlights
属性在 CSS
命名空间中访问,
它表示所有为自定义高亮注册的对象,适用于当前全局对象的相关文档。
它是类似映射的结构,并可使用常规方法进行更新。
它的映射条目最初是空的。
如果自定义高亮 位于高亮注册表中, 则称其为已注册。 如果之后被移除,它将停止成为已注册状态。
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
。
当自定义高亮用于强调语法错误时,建议将其类型设置为grammar-error
。
对于其他使用场景,建议将type
保持为highlight
。
用户代理应将自定义高亮提供给辅助技术使用。
当通过特定平台的辅助技术 API 暴露高亮时,
用户代理应尽可能具体地传达由其type
属性指定的高亮语义。
注意:例如,
如果平台的辅助技术 API 能够具体表示拼写错误和语法错误,
则期望用户代理使用这些功能来传达带有spelling-error
和spelling-error
的高亮的语义。
如果辅助技术 API 只能表达拼写错误,
则用户代理应使用拼写错误语义来传达拼写错误和语法错误类型的高亮。
如果辅助技术 API 无法支持表达拼写或语法错误,
则用户代理会将所有高亮暴露为通用的highlight
,
而不考虑它们的实际type
。
注意:选择此初始类型集
是因为它们被认为是 Highlight API 的流行使用案例,
并且今天的平台辅助技术 API 已经有一些支持来表达它们的语义。
辅助技术 API 当前无法表达其他预期的 Highlight API 使用案例的特定语义。
随着辅助技术 API 逐渐支持表达 Highlight API 的更多流行使用案例,
将来可能会向HighlightType
中添加更多类型。
5. 响应更改
5.1. 重绘
在高亮注册表中 添加或移除自定义高亮, 或者在已注册的自定义高亮中 添加或移除范围, 必须导致用户代理重新评估渲染结果,并在适当时进行重绘。
用户代理还必须根据作者对priority
属性
的更改或对已注册的自定义高亮中
范围边界点的更改,
在需要时重绘高亮。
我们应如何指定此重新评估的时间(及同步性)?[Issue #4596]
5.2. 范围更新和失效
作者可以使用Range
或StaticRange
来构建自定义高亮。
生成的自定义高亮表示文档的相同部分, 并且可以采用相同的样式设置。 但是,如果修改了底层文档,行为会有所不同。
Range
是活动范围。
用户代理会根据与该范围或其边界重叠的 DOM 更改调整Range
的
边界点,
并相应地重绘。边界点也可以由作者修改。
另一方面,
用户代理不能根据 DOM 更改调整StaticRange
的边界点,
也不能在创建后由作者修改。
用户代理应存储实际的StaticRange
,
而不是用活动的Range
来支持它们。
Range
对象以响应 DOM 修改会带来显著的性能成本。
如果作者打算监控 DOM 更改并根据更改调整或重新创建其自定义高亮中的范围,
强烈建议使用StaticRange
,
以避免这一步骤的高成本但不必要的操作。
相反,使用StaticRange
的作者应监控并响应 DOM 更改,
通过丢弃无效的范围
或自定义高亮并重新创建新的范围。
在计算文档的渲染方式时, 如果任何起始节点 或结束节点 引用的范围 其阴影包含根不属于该文档, 用户代理必须忽略该范围。 如果任何StaticRange 在关联文档的高亮注册表中无效, 用户代理也必须忽略该范围。
自定义高亮中的StaticRange
与
[css-contain-2]的交互似乎存在问题:
在一个完全包含的元素上,
我们应期望该元素后代的 DOM 更改不会导致包含元素之外的元素失效、重新设置样式或重绘。
但是,如果静态范围的一个边界点位于包含的子树内,
而另一个边界点在子树之外,
且该子树中的 DOM 发生更改,
使得子树内的边界点不再指向有效节点,
那么整个范围应被忽略,
这将影响包含子树之外的渲染。
这是样式封装的弱点,
还是上述失效逻辑的弱点,
亦或其他问题?[Issue #4598]
6. 事件处理
事件部分待定, 基于 https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/master/highlight/events-explainer.md
自定义高亮是否应有专门的事件处理机制, 还是应该将其添加到伪元素的通用处理机制中?
附录 C. 变更记录
本节为非规范性内容。
自2020年12月8日工作草案以来的变更
除了各种编辑改进和小的调整,主要更改包括:
-
将
HighlightsRegister
重命名为HighlightRegistry
-
从
HighlightRegistry
中移除了冗余的add()
方法。 (见 Issue 6092) -
使自定义高亮覆盖层堆叠在原生高亮覆盖层下方。 (见 Issue 4595)
-
使用整数处理高亮优先级,而不是浮点数。 (见 Issue 4592)
-
定义高亮优先级的默认值为0。 (见 Issue 6136)
-
将 HighlightRegistry 改为 maplike(而非 setlike),并移除 Highlight 的
name
属性。 (见 Issue 5910) -
澄清了来自错误窗口的范围不会被绘制。 (见 Issue 6417)
-
指定自定义高亮没有用户代理样式。 (见 Issue 6375)
-
推迟到[DOM]规范处理范围失效 (见 Issue 4597)
-
为
type
属性增加了支持不同高亮的清晰语义,以便向辅助工具暴露高亮。 (见 Issue 6498)
自2020年10月22日工作草案以来的变更
自2020年10月22日工作草案以来仅进行了编辑修改; 见差异。