如何突出显示 HTML 文本而不用标签包裹它?

发布于 2024-08-13 01:56:26 字数 204 浏览 9 评论 0原文

是否可以在不使用 或任何其他标签包装的情况下突出显示 HTML 文档中的文本?

例如,在 HTML 代码

The Quick Fox

中,我想突出显示 quick,但不在其周围添加 DOM 元素。将 DOM 元素添加到父元素就可以了。

谢谢!

Is it possible to highlight text in an HTML document using without wrapping it with <span> or any other tag for that matter?

For example, in the HTML code <p>The quick fox</p> I would like to highlight quick but without adding a DOM element around it. Adding a DOM element to a parent element is fine.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

卖梦商人 2024-08-20 01:56:26

不,不可能以可移植的方式跨所有浏览器。

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

编辑:在 2019 年左右开始的 Chrome 中,您可以使用滚动到文本片段,但这不是 HTML或浏览器标准功能。

No, it is not possible in a portable manner across all browsers.

You can't tell the browser to render a piece of text differently without inherently changing the DOM, regardless of whether you do it statically or dynamically (with Javascript, for example, as a post processing step).

Edit: In Chrome starting around 2019, you can use Scroll To Text Fragment, but this is not an HTML or browser standard feature.

鹿! 2024-08-20 01:56:26

CSS 自定义突出显示 API

浏览器供应商正在努力明确支持突出显示模式 - 提供令人难以置信的多功能新“自定义突出显示”API。

自版本 105 (2022-09-02) 起,此功能已在 Chrome 衍生产品中提供,目前 Firefox Nightly 也支持此功能。

https://developer.mozilla.org/en-US/docs/ Web/API/CSS_Custom_Highlight_API

请注意我的回答,我以较长的形式对此进行了描述:

https ://stackoverflow.com/a/78051161/16376459

现在有多种方法可以在不修改 DOM 元素结构的情况下实现这种效果。

然而,它们都有自己的警告。

这个答案来自我对开源浏览器扩展标记我的搜索(存储库),它突出显示您在网页上搜索的文本。所接受的方法的自然破坏性导致我考虑了许多替代方案。如果未选中高级选项“使用经典突出显示”,则扩展程序将仅使用以下方法之一,由于我将要描述的缺点,这不是默认选项。

下面的所有方法仅涉及添加额外的绘画DOM,因此无法设置文本样式或以其他方式更改现有内容。然而,它们完全是非破坏性操作,可以提高效率,使其成为具有竞争力的替代方案。

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

方法

  1. 整体式

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

    • 将所有突出显示绘制到元素上。

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

    优点:

    • 简单、明显。
    • 所有突出显示都在一个位置进行管理。
    • 对于同时突变多个元素非常有效。

    缺点:

    • 许多妥协。如果您选择覆盖突出显示,突出显示的不透明度必然是可见性和模糊文本之间的折衷。如果使用底图,则必须删除所有背景以避免隐藏高光。
    • 对于少数元素的突变效率很低,因为所有内容都必须立即重新计算(通过缓存部分缓解)。
    • 对于在小型、不同的框架中表现最佳的实现来说效率低下。
  2. 复合

    • 要突出显示所有文本,请查看包含部分文本范围的最低级别的不同元素。

    • 在每个元素上突出显示。

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

    优点:

    • 解决了元素背景隐藏高光的问题。
    • 布局变化将在一定程度上带来突出显示,从而减少警惕更新/重新计算的需要。
    • 更适合底层而不是覆盖,因此在模糊文本和强调文本之间的妥协更少。
    • 对于少数元素的更新非常有效,因为突出显示是模块化的并分解为一系列不同的区域。

    缺点:

    • 复杂、细致。
    • 在某些实现中,每个突出显示的元素仍然可能会丢失一个背景(通常为 background-image)。
    • 同时突变多个元素效率低下
    • 对于在大而少的帧中表现最佳的实现来说效率低下。
  3. 混合

    • 将突出显示职责合并到几个元素中(例如,使用 display: block 处理其 display: inline 子元素的突出显示)。

实现

  1. 元素

    • 创建适当大小的突出显示元素。
    • 将它们放置在绝对能够覆盖预期区域的位置。

    优点:

    • 完美的浏览器支持。
    • 提供无限效果。

    缺点:

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

    • 对绘制突出显示布局元素的 SVG 进行百分比编码。
    • 在祖先元素的 background-image 上使用 url()

    优点:

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

    缺点:

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

    • 对绘制布局突出显示元素的画布进行百分比编码,或使用 元素。
    • 在祖先元素的 background-image 上使用 url()

    优点:

    • 与现代浏览器兼容。

    缺点:

    • 仅在几张画布(尤其是大画布)后速度非常慢,大概是因为每个像素都被显式绘制。似乎优化不佳,特别是对于透明像素。
    • 如果使用url()页面 CSP 将(很少)阻止 url() 的使用,期望它提取远程内容内容。
  4. 胡迪尼

    • 注册一个 Paint 工作集,根据 CSS 变量的大小和位置以及您认为必要的任何其他信息绘制高光(请参阅 Houdini Paint API 的详细信息)。
    • 根据需要在元素上使用 background-image:paint({worklet name}) 以显示突出显示。
    • 在同一元素上定义 CSS 变量(工作集使用的变量) - 考虑为此使用字符串化的 JSON 对象,该对象将在工作集内解包,效率非常高。
  5. 元素()

    • 创建适当大小的突出显示元素。
    • 绝对将它们放置在覆盖预期区域的位置,位于 DOM 的专用部分(屏幕外)。
    • 根据需要在元素上使用 background: element({突出显示元素的 ID}) 以显示突出显示。

    优点:

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

    缺点:

    • 特定于 Firefox,它处于实验阶段,但已获得支持多年。
    • 出于某种原因,需要应用到 background 而不仅仅是 background-image,导致更多地覆盖元素背景。这似乎是一个疏忽。
    • 重量级,有点慢,依赖 DOM。

动态更新

我不会详细介绍这一点,因为它非常适合您的需求。但是,为了在应用于整个页面时获得完全最新的突出显示,我需要:

  • A MutationObserver 观察 subtreechildList 和文档正文的 characterData,以检测可能影响匹配的所有更改。我的情况很极端,所以我必须观察整个文件;我使用各种方法进行优化,包括过滤掉不需要关注的事件。

  • 一个 ResizeObserver 观察当前视图中突出显示的元素,以便在布局发生变化时重新计算突出显示。

  • 一个 IntersectionObserver 观察所有突出显示的元素何时出现在视口中,以便仅在需要时绘制突出显示和 ResizeObserver这只是一项优化措施。

信息

这里是 PR,我使用上述多种可以替换的方法实现了这一点。我使用了一种高度结构化的方法 - 涉及跨 DOM 的缓存和其他优化,例如将其分解为多个阶段 - 即使在所有平均页面都以各种颜色突出显示的情况下也能使其高效。 PR 中提供了概述。

这是最极端的可能情况之一,其他人应该完全有可能在不显着性能损失的情况下取得良好的结果。

我很乐意回答有关我的实施或开始使用这些技术的任何问题。

文章

注意

*由于在客户端矩形与实际布局+位置关联时浏览器行为复杂,因此很难确保基于覆盖的突出显示算法在所有情况下都能正确定位突出显示框。我仍然无法正确解释边界的影响(这会扰乱计算)或流内容的奇怪情况,但仅使用 Range.getClientRects() 的结果在技术上是可行的Element.getClientRects()

CSS Custom Highlight API

Browser vendors are working towards explicit support for the highlighting pattern - with an incredibly versatile new "Custom Highlight" API.

This has been available in Chrome derivatives since version 105 (2022-09-02), and is currently supported in Firefox Nightly.

https://developer.mozilla.org/en-US/docs/Web/API/CSS_Custom_Highlight_API

Note my answer here, where I describe this in longer form:

https://stackoverflow.com/a/78051161/16376459

There are now multiple ways of achieving this effect without modifying the element structure of the DOM.

They all, however, come with their own caveats.

This answer is derived from research I performed for my open source browser extension Mark My Search (repository), which highlights text you search for on webpages. The natural destructiveness of the accepted approach led to my consideration of many alternatives. The extension will only use one the below approaches if the advanced option "Use CLASSIC highlighting" is unchecked, which is not default due to the disadvantages I am about to describe.

All of my approaches below only involve adding additional painting to the DOM, so there is no option to style text or otherwise change existing content. However, they are entirely non-destructive operations which can result in efficiency gains, making them a competitive alternative.

Important: The assumption through this answer is that you either apply styling directly to elements, or use a stylesheet and generated attributes to apply it remotely. This type of DOM manipulation is still required.

Approaches

  1. Monolithic

    • Overlay the entire content area with a single element.

    • Draw all highlighting onto the element.

      • Get client rects of the required ranges of text.
      • Compare with the client rects of the overall element, obtaining the relative drawn text positions.*
      • Use one of the implementations below.

    Pros:

    • Simple, obvious.
    • All highlighting managed in one place.
    • Efficient for mutations of many elements at once.

    Cons:

    • Many compromises. If you choose to overlay the highlights, opacity of highlighting is necessarily a compromise between visibility and obscuring text. If you underlay, you must remove all backgrounds to avoid highlights being hidden.
    • Inefficient for mutations of few elements, as everything must be recalculated at once (partially mitigated by caching).
    • Inefficient for implementations that perform best in small, disparate frames.
  2. Composite

    • For all text to be highlighted, look at the lowest-level distinct elements which contain parts of the text ranges.

    • Draw highlighting onto each of these elements.

      • Get client rects of the required ranges of text.
      • Compare with the client rects of the specific containing element, obtaining the relative drawn text positions.*
      • Apply one of the implementations below to the containing element.

    Pros:

    • Removes problem of highlights being hidden by element backgrounds.
    • Layout shifts will to an extent carry highlighting with them, reducing the need for vigilant updates/recalculations.
    • Much more pertinent to underlay instead of overlay, so less compromise between obscuring text and emphasising it.
    • Efficient for updates of few elements, as highlighting is modular and decomposed into a range of distinct areas.

    Cons:

    • Complex, nuanced.
    • Under some implementations, one background (normally background-image) is still potentially lost per highlighted element.
    • Inefficient for mutations of many elements at once
    • Inefficient for implementations that perform best in large, few frames.
  3. Hybrid

    • Combine highlighting responsibility into a few elements (for example, ones with display: block to handle highlights for its display: inline children).

Implementations

  1. Elements

    • Create highlighting elements of appropriate sizes.
    • Absolutely position them such that they cover the intended areas.

    Pros:

    • Faultless browser support.
    • Infinite effects are available.

    Cons:

    • Heavyweight, slow, DOM-reliant.
    • Adds many superfluous elements to the main structure of the page, disruptive.
  2. SVG

    • Percent-encode an SVG drawing the laid out highlighting elements.
    • Use url() on the background-image of the ancestor element.

    Pros:

    • Pretty fast.
    • Compatible with modern browsers.

    Cons:

    • Page CSPs will (rarely) block the use of url(), expecting it to extract remote content.
  3. Canvas

    • Percent-encode a canvas drawing the laid out highlighting elements, or use a <canvas> element.
    • Use url() on the background-image of the ancestor element.

    Pros:

    • Compatible with modern browsers.

    Cons:

    • Extremely slow after only a few canvases (especially large ones), presumably due to each pixel being explicitly painted. Seems to be poorly optimised, particularly for transparent pixels.
    • If url() is used: Page CSPs will (rarely) block the use of url(), expecting it to extract remote content.
  4. Houdini

    • Register a Paint worklet that draws highlights based on sizing and positioning CSS variables, with whatever additional info you deem necessary (see details of Houdini Paint API).
    • Use background-image: paint({worklet name}) as appropriate on elements to display highlighting.
    • Define CSS variables (the ones consumed by the worklet) on the same element - consider using a stringified JSON object for this which will be unpacked within the worklet, it's surprisingly efficient.
  5. element()

    • Create highlighting elements of appropriate sizes.
    • Absolutely position them such that they cover the intended areas, in a dedicated portion of the DOM (offscreen).
    • Use background: element({ID of highlighting element}) as appropriate on elements to display highlighting.

    Pros:

    • Infinite effects are possible.
    • Much more viable than the direct DOM alternative.

    Cons:

    • Specific to Firefox, on which it is experimental but has been supported for years.
    • For some reason requires application to background instead of just background-image, resulting in more overriding of element backgrounds. This seems to be an oversight.
    • Heavyweight, a little slow, DOM-reliant.

Dynamically Updating

I won't go into much detail about this, as it is very specific to your needs. However, to obtain fully up-to-date highlighting when applied to an entire page, I needed:

  • A MutationObserver observing subtree, childList, and characterData for the document body, in order to detect all changes that may affect matching. My case is very extreme, which is why I must observe the entire document; I optimised using various methods, including filtering out events that did not require attention.

  • A ResizeObserver observing highlighted elements currently in view, in order to recalculate highlighting when layout shifts occur.

  • An IntersectionObserver observing all highlighted elements for when they appear in the viewport, in order to draw highlighting and the ResizeObserver only when needed. This is an optimisation measure only.

Information

Here is the PR in which I achieved this, using multiple of the above methods which could be swapped out. I used a highly structured approach - involving caching across the DOM and other optimisations, such as decomposing it into multiple stages - to make it efficient even for cases where all of an average page has been highlighted in various colours. An overview is presented in the PR.

This being one of the most extreme possible cases, it should be entirely possible for others to achieve good results without significant performance loss.

I am happy to answer any questions regarding my implementation, or getting started with these technologies.

Articles

Notes

*Due to convoluted browser behaviour when it comes to the association of client rects with actual layout+position, it is very difficult to make sure an overlay-based highlighting algorithm will position highlight-boxes correctly in all cases. I have still not managed to correctly account for the effect of border (which disrupts calculations) or strange cases of flow content, but it is technically possible using only the results of Range.getClientRects() and Element.getClientRects().

烏雲後面有陽光 2024-08-20 01:56:26

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

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

It is possible if you use an absolutely positioned element with a transparent repeating background image or a transparent background color (using rgba or hsla) and position it over the selected area.

Another way to do it would be to have an absolutely positioned canvas element without a background that takes up the whole browser viewport and draw a transparent rectangle over the selection.

江南月 2024-08-20 01:56:26

这是不可能的。

如果您只想在原始源代码中没有标签,则可以稍后使用 Javascript 魔法添加标签。你可以做类似的事情

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

,编写一个 JQuery/Prototype/plain JS 函数来动态突出显示它,但是为什么呢?如果你详细说明一下,有人可能会想出一个主意。

It's not possible.

If you just want no tags in the original source code, it might be possible by adding tags later using Javascript magic. You could do something like

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

and write a JQuery/Prototype/plain JS function to highlight it on the fly, but what for and why? If you elaborate a bit, someone may come up with an idea.

嘦怹 2024-08-20 01:56:26

比我想象的唯一方法是使用 元素,并完全手动渲染所有内容。

The only way to do this than I can imagine would be to use the <canvas> element, and render absolutely everything by hand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文