CSS Custom Highlight API

Problem

Ever tried building a search and highlight feature? Like, when you type something in a search box, and it shows a list of results with the matching text highlighted?

scheme of how css custom highlight api works

The most challenging part of this feature is the highlighting itself. On every keydown, you need to highlight the matching text in the DOM. But how do you actually do that? You can only apply CSS styles using selectors like class, attribute, or tag. But with plain text, there's nothing to hook into—just the text itself. That means, while the user is typing, we need to modify the DOM by wrapping the matching text in HTML tags like:

<span class="to-be-highlighted">

And that's the most annoying part—we have to modify the DOM every time the user types.

Sometimes it can even lead to unexpected results.

For example, I was working on a project that used the Angular framework. We had this search and highlight feature there, but the text that needed to be highlighted was coming from Angular interpolation.

Like:

Then I found out when the textProvider variable changed - the view didn’t update at all—as if nothing had happened.

After digging a bit, I realized that interpolation only stopped working after performing a search in the search bar. The reason was that the text inside the <span> tags was being replaced using the innerHTML property, which happens outside of Angular's context. Angular just loses the connection after that kind of DOM intervention.

Solution

There's a CSS Custom Highlight API that lets you highlight text ranges in the DOM. The best part? You can do it without modifying the DOM! Without this API, your only option is to wrap the text you want to highlight in an HTML tag like span, using the innerHTML property, as mentioned above. Try searching online—you’ll find tons of solutions that rely on that approach.

Now let’s take a closer look at the Highlight API.

How CSS Custom Highlight API works

Let’s see what this API is made of and how its parts work together.

Range object

First, you need a way to specify the text you want to highlight. You can do that using Range object(s) .

Highlight object

Then the Range(s) need to be added to a Highlight object . You can think of it like a Set object —or more accurately, a Set-like object .

Highlight Registry

The third step is to register the Highlight object in the HighlightRegistry , which is part of the CSS registry. Think of this registry as a bridge between the text you want to highlight and the styles you want to apply to it.

Take a look at the code—things should start to make more sense:

Define highlight styles

The last step is to define the styles you want to apply to your custom highlight selector (see the previous step). You can do this using the ::highlight pseudo-element.

Scheme

The only thing the developer needs to control is the Range objects registered in a Highlight object.

Why is that, you may ask? What’s the problem?

The key point is that a single page can have multiple search + highlight UIs running at the same time. But there's only one Highlight object. So when a user clears a search bar, the app should remove only the Range objects that were registered by that specific search UI—so it doesn't interfere with other search + highlight UIs on the page.

Take a look at the image below. It shows two search bars, both controlled by a single Highlight object.

Since both search bars are controlled by a single Highlight object, we need to track which one registers each Range object. That way, when a user clears a search bar, the code removes only the Range(s) associated with that specific search + highlight instance.

Downsides

This is great API which allows to prevent DOM modifications. But there are two things which can prevent you to use it:

  • Browser support - in a time of writing Firefox still does not support it. They have a registered bug which was created years ago but still in development.
  • Animation - it seems that there is no way to animate process of highlighting.

Github example

If you are using Angular framework then you can find an example here:

CSS Custom Highlight API through Angular Directive

It's not that hard at all. Try it.