网页文本样式化一直是开发者关注的焦点。令人期待的是CSS Custom Highlight API的出现,它将革新网页文本范围样式化。
举例来说,Google Docs、Word或Dropbox Paper等文本编辑软件会检测拼写和语法错误,并在下方显示波浪线以提示用户。VS Code等代码编辑器对代码错误也采用类似方法。
另一个常见的文本高亮用例是搜索和高亮功能,用户在输入框中输入文本,页面会搜索匹配结果并高亮显示。现在,您可以在浏览器中尝试按下Ctrl/⌘ F,然后输入本文中的部分文本。
浏览器通常会自动处理这些样式情况。可编辑区域(如)会自动显示拼写错误的波浪线。查找命令会自动高亮显示找到的文本。
但是,如果我们想自己进行这种样式化怎么办?长期以来,如何在网页上实现这一点一直是一个常见问题,它可能浪费了许多人大量的时间。
这不是一个简单的难题。我们不仅仅是用带有class的<span></span>
标签包裹文本并应用一些CSS。实际上,这需要能够正确地高亮显示跨越任意复杂DOM树的多个文本范围,并且可能跨越DOM元素的边界。
解决这个问题的常见方法有两种:
我们首先回顾一下这两种方法,然后看看即将推出的CSS Custom Highlight API如何改变这一切。
最著名的可样式化文本范围可能是用户选择。当您使用指向设备选择网页上的文本时,会自动创建一个Selection对象。事实上,现在尝试选择此页面上的文本,然后在DevTools控制台中运行document.getSelection()
。您应该会看到所选文本的位置信息。
事实证明,您还可以通过JavaScript以编程方式创建文本选择。以下是一个示例:
// 首先,创建一个Range对象。 const range = new Range(); // 并设置其起始和结束位置。 range.setStart(parentNode, startOffset); range.setEnd(parentNode, endOffset); // 然后,将当前选择设置为此范围。 document.getSelection().removeAllRanges(); document.getSelection().addRange(range);
最后一块拼图是样式化此范围。CSS有一个名为::selection
的伪元素可以做到这一点,并且它在所有浏览器中都受支持。
::selection { background-color: #f06; color: white; }
以下是一个使用此技术依次高亮显示页面中所有单词的示例:
除了::selection
伪元素之外,还有许多其他伪元素:
::target-text
选择浏览器中已滚动到的文本(支持滚动到文本功能的浏览器)。(MDN)::spelling-error
选择浏览器标记为包含拼写错误的文本。(MDN)::grammar-error
选择浏览器标记为包含语法错误的文本。(MDN)不幸的是,这里的浏览器支持不是很好,尽管这些范围本身很有用,但它们不能用于样式化自定义文本片段——只能样式化浏览器预定义的文本片段。
因此,用户文本选择很好,因为它相对容易实现,并且不会更改页面的DOM。实际上,Range对象本质上是页面中段落的坐标,而不是需要创建才能存在的HTML元素。
然而,一个主要的缺点是,创建选择会重置用户已经手动选择的任何内容。尝试在上面的演示中选择文本以测试这一点。您会看到代码将选择移动到其他位置时,它会消失。
如果您使用Selection对象不足以满足您的需求,那么第二个解决方案几乎是唯一的选择。此解决方案围绕着自己动手做所有事情,使用JavaScript在您希望高亮显示出现的位置插入新的HTML元素。
不幸的是,这意味着需要编写和维护更多的JavaScript代码,更不用说每当高亮显示更改时,它都会强制浏览器重新创建页面的布局。此外,还有一些复杂的边缘情况,例如,当您想高亮显示跨越多个DOM元素的文本片段时。
有趣的是,CodeMirror和Monaco(为VS Code提供支持的JavaScript文本编辑器库)有自己的高亮逻辑。它们使用稍微不同的方法,其中高亮显示包含在DOM树的单独部分中。文本行和高亮显示的段落在DOM中的两个不同位置呈现,然后彼此定位。如果您检查包含文本的DOM子树,则没有高亮显示。通过这种方式,可以重新渲染高亮显示而不会影响文本行,并且不必在文本行中引入新的元素。
总的来说,感觉缺少一个浏览器支持的高亮显示功能。某些功能可以帮助解决所有这些缺点(不会干扰用户文本选择,支持多选,代码简单)并且比自定义解决方案更快。
幸运的是,这就是我们在这里要讨论的内容!
CSS Custom Highlight API是一个新的W3C规范(目前处于工作草案状态),它使得可以从JavaScript样式化任意文本范围!这里的方法与我们前面回顾的用户文本选择技术非常相似。它为开发人员提供了一种方法来创建任意范围(来自JavaScript),然后使用CSS对其进行样式化。
第一步是创建要高亮显示的文本范围,这可以使用JavaScript中的Range来完成。因此,就像我们在设置当前选择时所做的那样:
const range = new Range(); range.setStart(parentNode, startOffset); range.setEnd(parentNode, endOffset);
值得注意的是,如果作为第一个参数传递的节点是文本节点或不是文本节点,则setStart
和setEnd
方法的工作方式不同。对于文本节点,偏移量对应于节点中的字符数。对于其他节点,偏移量对应于父节点中的子节点数。
还值得注意的是,setStart
和setEnd
并不是描述范围起始和结束位置的唯一方法。查看Range类上可用的其他方法以查看其他选项。
第二步包括为上一步中创建的范围创建Highlight对象。Highlight对象可以接收一个或多个Range。因此,如果您想以完全相同的方式高亮显示许多文本片段,则可能应该创建一个Highlight对象并使用与这些文本片段对应的所有Range来初始化它。
const highlight = new Highlight(range1, range2, ..., rangeN);
但是您也可以根据需要创建任意数量的Highlight对象。例如,如果您正在构建一个协作文本编辑器,其中每个用户获得不同的文本颜色,那么您可以为每个用户创建一个Highlight对象。然后可以对每个对象进行不同的样式化,正如我们接下来将看到的。
现在,Highlight对象本身不会执行任何操作。它们首先需要在所谓的突出显示注册表中注册。这是使用CSS Highlights API完成的。注册表就像一个地图,您可以通过为高亮显示指定名称来注册新的高亮显示,以及删除高亮显示(甚至清除整个注册表)。
以下是注册单个高亮显示的方法。
CSS.highlights.set('my-custom-highlight', highlight);
其中my-custom-highlight
是您选择的名称,highlight
是上一步中创建的Highlight对象。
最后一步是实际样式化已注册的高亮显示。这是使用新的CSS ::highlight()
伪元素完成的,使用您在注册Highlight对象时选择的名称(在我们的示例中为my-custom-highlight
)。
::highlight(my-custom-highlight) { background-color: yellow; color: black; }
值得注意的是,就像::selection
一样,只有一部分CSS属性可以与::highlight()
伪元素一起使用:
background-color
caret-color
color
cursor
fill
stroke
stroke-width
text-decoration
(这可能只在规范的版本2中受支持)text-shadow
有多种方法可以更新页面上的高亮显示文本。
例如,您可以使用CSS.highlights.clear()
完全清除高亮显示注册表,然后从头开始。或者,您还可以更新底层范围,而无需重新创建任何对象。为此,再次使用range.setStart
和range.setEnd
方法(或任何其他Range方法),浏览器将重新绘制高亮显示。
但是,Highlight对象的工作方式类似于JavaScript Set,这意味着您还可以使用highlight.add(newRange)
将新的Range对象添加到现有的Highlight中,或者使用highlight.delete(existingRange)
删除Range。
第三,您还可以向CSS.highlights注册表添加或删除特定的Highlight对象。由于此API的工作方式类似于JavaScript Map,您可以使用set
和delete
来更新当前已注册的Highlights。
CSS Custom Highlight API的规范相对较新,其在浏览器中的实现仍不完整。因此,尽管这将成为Web平台的一个非常有用的补充,但它还不太适合用于生产环境。
Microsoft Edge团队目前正在Chromium中实现CSS Custom Highlight API。事实上,通过启用实验性Web平台功能标志(在about:flags下),现在就可以在Canary版本中使用此功能。目前还没有关于该功能何时将在Chrome、Edge和其他基于Chromium的浏览器中发布的明确计划,但它已经非常接近了。
Safari 99 也支持此API,但在实验标志(Develop → Experimental Features → Highlight API)之后,并且接口略有不同,因为它使用StaticRange对象而不是Range对象。
Firefox尚不支持此API,不过您可以阅读Mozilla关于此API的立场以了解更多信息。
说到Microsoft Edge,他们已经设置了一个演示,您可以在其中试用CSS Custom Highlight API。但在尝试演示之前,请确保您使用的是启用了about:flags页面中实验性Web平台功能标志的Chrome或Edge Canary。
/button 查看演示
该演示使用Custom Highlight API根据您在页面顶部搜索字段中输入的内容来高亮显示页面中的文本范围。
页面加载后,JavaScript代码检索页面中的所有文本节点(使用TreeWalker),当用户在搜索字段中键入内容时,代码会迭代这些节点,直到找到匹配项。然后使用这些匹配项创建Range对象,然后使用Custom Highlight API对其进行高亮显示。
那么,这个新的浏览器提供的高亮显示API真的值得吗?绝对值得!
首先,即使CSS Custom Highlight API起初看起来有点复杂(例如,必须创建范围,然后是高亮显示,然后注册它们,最后样式化它们),它仍然比创建新的DOM元素并将它们插入到正确的位置要简单得多。
更重要的是,浏览器引擎可以非常非常快速地样式化这些范围。
仅允许使用一部分CSS属性与::highlight()
伪元素一起使用的理由是,这部分属性只包含浏览器可以非常有效地应用的属性,而无需重新创建页面的布局。通过在页面周围插入新的DOM元素来高亮显示文本范围,需要引擎做更多工作。
但不要相信我的话。Fernando Fiori参与了该API的开发,他创建了这个不错的性能比较演示。在我的电脑上,CSS Custom Highlight API的性能平均比基于DOM的高亮显示快5倍。
由于Chromium和Safari已经提供了实验性支持,我们正越来越接近可以在生产环境中使用的东西。我迫不及待地想看到浏览器一致地支持Custom Highlight API,并看看这将解锁哪些功能!
以上是CSS自定义突出显示API:首次查看的详细内容。更多信息请关注PHP中文网其他相关文章!