WebCraft

::highlight()

Marking text without touching the DOM

Live demo

A range registered with the browser, painted where it falls, and removed again without a single element being wrapped, rebuilt or re-rendered.

Caveat and fallback

CaveatA custom highlight accepts a short list of properties and nothing else: color, background-color, the text decoration set and text-shadow. No padding, no border, no radius. The ranges are yours to maintain too, so any edit to the text leaves them pointing at the wrong offsets, and because nothing in the DOM changed a screen reader learns nothing from the marking.

FallbackTest for CSS.highlights before you build a single range; where it is missing the paragraph simply never lights up, which is the right way to lose a search affordance. Wrapping matches in <mark> still works and is still the better answer when the marking is part of the content rather than a passing state of the interface.

Source code

HTML + CSS + JS
<p>a range painted where it falls</p>

::highlight(hits) {
  background-color: #6fa8dc6b;
  color: #e6eaf2;
}

// no wrapper elements, no reflow
const r = new Range();
r.setStart(node, 2); r.setEnd(node, 7);
CSS.highlights.set('hits', new Highlight(r));

Published

Questions

<details name>

How do I highlight search results without wrapping them in span elements?

Build a Range for each match and register them with CSS.highlights.set('hits', new Highlight(...ranges)), then style ::highlight(hits). Nothing in the document changes, so there is no reflow and no wrapper to unpick afterwards.

Why can I not give a custom highlight a border or padding?

A highlight pseudo-element paints over existing text rather than creating a box, so only color, background-color, the text decoration properties and text-shadow apply. Anything that would change layout is ignored by design, which is also why highlighting cannot make the text move.

Do screen readers announce a custom highlight?

No. The DOM is untouched, so assistive technology has nothing new to read. Use it for transient interface state such as a live search, and keep real markup like <mark> for emphasis that is part of the content.