如何在不使用标签包裹文本的情况下突出显示HTML文本?

6

有没有可能在HTML文档中使用不包装<span>或其他标签的情况下突出显示文本?

例如,在HTML代码<p>The quick fox</p>中,我想突出显示quick,但不添加DOM元素。向父元素添加DOM元素是可以的。

谢谢!


1
我想用边界框(bounding boxes)来突出显示(highlight)多个搜索项。但是,假设有以下两个搜索项:“The quick”和“quick fox”。如果我通过包装(wrapping)来突出显示“The quick”,它将成为一个单独的文本节点,这将使查找“quick fox”变得困难。我正在考虑的一种技术是:
  1. 暂时添加一个<span />元素。
  2. 找到该元素的边界框(使用offset()、width、height)。
  3. 删除上述元素。
  4. 在容器中添加一个具有上述尺寸的框。
如果文本没有被包装,这种方法可能有效...
- Sleepster
5个回答

10

不,无法在所有浏览器中以可移植的方式实现。

您无法告诉浏览器在本质上更改DOM的情况下以不同的方式呈现文本,无论您是静态地还是动态地(例如使用Javascript作为后处理步骤)。

编辑:在Chrome 2019年左右开始,您可以使用Scroll To Text Fragment,但这不是HTML或浏览器标准功能。


1
如果您在透明画布覆盖层中更改像素,则无需更改DOM。 - Eli Grey
你如何在不改变DOM的情况下获取文本块的绝对位置? - Mike Atlas
将<span>元素添加到DOM中,找出它的位置,然后删除并规范化(normalize())。 - Sleepster
如果他们调整浏览器窗口大小会怎样? - Mike Atlas
我想知道如何实现这种高亮显示的方法。 - Lerner Zhang
显示剩余4条评论

4

如果您使用具有透明重复背景图像或透明背景颜色(使用rgba或hsla)的绝对定位元素,并将其定位在所选区域上方,则可以实现。

另一种方法是使用没有背景的绝对定位画布元素,该元素占用整个浏览器视口并在所选内容上绘制透明矩形。


你如何在不改变DOM的情况下获取文本的绝对位置? - Mike Atlas
1
将<span>元素添加到DOM中,找出它的位置,然后删除并规范化(normalize())。 - Sleepster

4

现在有多种方法可以实现此效果,而无需修改DOM元素结构。

然而,它们都有自己的注意事项。

这个答案是基于我为我的开源浏览器扩展程序Mark My Search(repository)所做的研究得出的,该扩展程序可以在网页上突出显示您搜索的文本。被接受的方法的自然破坏性导致我考虑了许多替代方案。如果未选中高级选项“使用经典突出显示”,则扩展程序将仅使用以下一种方法,这不是默认设置,因为我将要描述的缺点。

我下面的所有方法都只涉及向DOM添加额外的绘画,因此没有样式文本或以其他方式更改现有内容的选项。但是,它们是完全非破坏性的操作,可以带来效率提升,使它们成为竞争对手的替代方案。

重要提示:本答案的假设是您直接将样式应用于元素,或使用样式表和生成的属性远程应用样式。仍需要此类型的DOM操作。

方法

  1. 单体式

    • 用一个元素覆盖整个内容区域。

    • 将所有高亮绘制到该元素上。

      • 获取所需文本范围的客户端矩形。
      • 与整个元素的客户端矩形进行比较,获取相对绘制文本位置*。
      • 使用下面的其中一种实现。

    优点:

    • 简单明了。
    • 所有高亮都在一个地方管理。
    • 对于同时变异多个元素非常高效。

    缺点:

    • 许多妥协。如果选择覆盖高亮,则高亮的不透明度必须在可见性和遮挡文本之间做出妥协。如果是底部叠加,则必须删除所有背景以避免隐藏高亮。
    • 对于少量元素的变异效率低下,因为必须一次重新计算所有内容(通过缓存部分减轻)。
    • 对于在小而不同的框架中表现最佳的实现效率低下。
  2. 组合式

    • 对于所有要高亮的文本,查看包含文本范围部分的最低级别不同元素。

    • 在每个这些元素上绘制高亮。

      • 获取所需文本范围的客户端矩形。
      • 与特定包含元素的客户端矩形进行比较,获取相对绘制文本位置*。
      • 将其中一个实现应用于包含元素。

    优点:

    • 消除了高亮被元素背景隐藏的问题。
    • 布局变化将在一定程度上带有高亮,减少了需要警惕更新/重新计算的需要。
    • 更适合底部叠加而不是覆盖,因此在遮挡文本和强调文本之间妥协较少。
    • 对于少量元素的更新非常高效,因为高亮是模块化的,并分解成一系列不同区域。

    缺点:

    • 复杂微妙。
    • 在某些实现下,每个高亮元素仍然可能丢失一个背景(通常是background-image)。
    • 对于同时变异许多元素效率低下。
    • 对于在大而少的框架中表现最佳的实现效率低下。
  3. 混合式

    • 将高亮责任合并到几个元素中(例如,具有display: block以处理其display: inline子元素的高亮)。

实现

  1. 元素

    • 创建适当大小的高亮元素。
    • 绝对定位它们以覆盖预期区域。

    优点:

    • 浏览器支持无误。
    • 可用无限效果。

    缺点:

    • 重量级、慢、依赖DOM。
    • 向页面主体结构添加许多多余的元素,具有破坏性。
  2. SVG

    • 百分号编码SVG图形中的布局高亮元素。
    • 在祖先元素的background-image上使用url()

    优点:

    • 相当快速。
    • 与现代浏览器兼容。

    缺点:

    • 页面CSP(内容安全策略)会(很少)阻止使用url(),期望它提取远程内容。
  3. 画布

    • 百分号编码一个画布,其中包含布局高亮元素,或使用<canvas>元素。
    • 在祖先元素的background-image上使用url()

    优点:

    • 与现代浏览器兼容。

    缺点:

    • 仅有几个画布后就会变得极慢(特别是大画布),可能是因为每个像素都被显式绘制。似乎没有得到很好的优化,特别是对于透明像素。
    • 如果使用url()页面CSP(内容安全策略)会(很少)阻止使用url(),期望它提取远程内容。
  4. Houdini

    • 注册一个Paint worklet,根据大小和定位CSS变量绘制高亮,并添加您认为必要的其他信息(请参阅Houdini Paint API的详细信息)。
    • 在要显示高亮的元素上适当使用background-image: paint({worklet name})
    • 在同一元素上定义CSS变量(由worklet消耗的变量)-考虑使用字符串化的JSON对象,这在worklet内部将被解包,效率出奇地高。
  5. element()

    • 创建适当大小的高亮元素。
    • 在DOM的专用部分(屏幕外)中绝对定位它们以覆盖预期区域。
    • 在要显示高亮的元素上适当使用background: element({ID of highlighting element})

    优点:

    • 无限效果可行。
    • 比直接DOM替代方案更可行。

    缺点:

    • 仅适用于Firefox,虽然它是实验性的,但已经支持多年。
    • 由于某种原因需要应用到background而不是只有background-image,导致更多地覆盖元素背景。这

      动态更新

      我不会详细介绍这个,因为它非常特定于您的需求。然而,为了在应用于整个页面时获得完全最新的高亮显示,我需要:

      • 一个观察文档主体的MutationObserver,观察subtreechildListcharacterData,以便检测可能影响匹配的所有更改。我的情况非常极端,这就是为什么我必须观察整个文档的原因;我使用了各种方法进行优化,包括过滤不需要关注的事件。

      • 一个观察当前可见的高亮元素的ResizeObserver,以便在布局发生变化时重新计算高亮显示。

      • 一个观察所有高亮元素的IntersectionObserver,以便在它们出现在视口中时绘制高亮显示和ResizeObserver,只有在需要时才进行。 这只是一项优化措施。

      信息

      这里是我使用多种上述方法(可替换)实现的PR,采用高度结构化的方法 - 包括跨DOM缓存和其他优化,例如将其分解成多个阶段 - 使其在各种颜色中突出显示整个平均页面的情况下也能高效运行。在PR中提供了概述。

      作为最极端的可能情况之一,其他人完全可以在不显著降低性能的情况下取得良好的结果。

      我很乐意回答有关我的实现或使用这些技术入门的任何问题。

      文章

      注释

      *由于浏览器在客户端矩形与实际布局和位置相关联时的复杂行为,很难确保基于覆盖的高亮算法在所有情况下都能正确定位高亮框。我仍然无法正确考虑边框效果(会干扰计算)或奇怪的流内容情况,但是只使用Range.getClientRects()Element.getClientRects()的结果技术上是可能的。.


0

这是不可能的。

如果你只是想在原始源代码中没有标签,那么可以通过后期使用Javascript魔法来添加标签。你可以做一些类似于:

<p highlight="quick">The quick fox</p>

编写一个JQuery/Prototype/plain JS函数来实时高亮它,但是为什么要这样做呢?如果您能详细阐述一下,其他人可能会想出一些想法。


我想知道如何实现这种高亮显示 - Lerner Zhang

0
我能想象的唯一方法是使用<canvas>元素,并手动渲染所有内容。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接