
Mainstream Approaches to Text Highlighting
Text highlighting might seem simple at first glance—just change the background to yellow—but it’s actually quite complex. Take this example below, where the highlighted portion spans multiple elements and contains partial text from elements below.
Below, I’ll walk through several common approaches to implementing text highlighting, discussing their pros and cons along with practical use cases to outline the mainstream technical solutions.
CSS Highlight API — Modern and Efficient Text Highlighting
This is currently the best choice for implementing text highlighting, being simple to use and performant. The only downside is that browsers need relatively recent versions for support, though by 2025 all major browsers support it.
Highlight Object
First, you need to create a Range
object for each highlighted area. Range is a long-standing API that represents a continuous section in the DOM tree—if you’re not familiar with it, you’ll need to brush up on it.
const range = new Range();
range.setStart(someNode, startOffset);
range.setEnd(someNode, endOffset);
Then add it to a Highlight
object. Since highlighting can have multiple non-contiguous areas, a single Highlight
object can contain several Ranges.
const highlight = new Highlight(range1, range2, ..., rangeN);
It’s similar to a Set object, so you can use methods like add
, delete
, etc., to modify the contained Ranges.
CSS.highlights
Then add the Highlight object to CSS.highlights
. It’s similar to a Map object, with methods for adding and removing members like get
, set
, delete
, etc., and the same iteration methods.
Just like a Map, when adding a Highlight, you need to give it a key as the highlight’s name. This name is very useful—as we’ll see below, we use this name to set the highlighting style, such as a green background. So all highlights in the same Highlight object have the same style. If you want to highlight another piece of text in blue, you’ll need to create a new highlight.
CSS.highlights.set("my-highlight", highlight);
Defining the ::highlight Pseudo-element
Next, CSS styles take the stage. You can define highlight styles using the ::highlight(highlight-name)
pseudo-element.
::highlight(my-highlight) {
background-color: yellow;
color: black;
}
Note: Only a few CSS properties can be used, such as
background-color
,color
,cursor
, etc. See the specification for details.
Dynamically Modifying Highlight Ranges
CSS.highlights
is like a Map with similar methods to update Highlights:
CSS.highlights.clear()
set(highlightName, Highlight)
delete(highlightName)
The Highlight
object is like a Set with similar methods to update Ranges:
Highlight.add(range)
delete(range)
Modifying Text Element Styles
Achieve highlighting by traversing the DOM to find target text nodes and directly modifying their background styles.
- Requires DOM modifications, causing browser reflow with significant performance overhead.
- Implementing cross-element highlighting (where the highlight range spans multiple elements) is quite complex.
- Suitable for scenarios where highlighted content is relatively fixed and the range is small.
Overlaying Additional Layers to Render Highlights Without Modifying Text DOM
Create a transparent overlay layer, calculate the screen position of target text, then draw semi-transparent highlights on the overlay.
Positioning Highlight Areas
- Use
Element.getClientRects()
orgetBoundingClientRect()
to get text coordinates on the page. - When handling multi-line text, each line corresponds to a rectangular area that needs separate highlight blocks.
- Window size changes or font size changes require recalculating highlight positions, which may impact performance.
Highlight Drawing Methods
DIV as Overlay
Use absolutely positioned parent containers and semi-transparent background child elements to overlay text. For example, the Monaco editor uses this approach to implement same-word highlighting.
<div class="editor" style="position: relative;">
<div class="overlay" style="position: absolute; height: 0;">
<div
class="highlight"
style="top:0; left:57px; width:23px; background-color: rgba(173,214,255,0.15);"
></div>
<!-- Add more highlight areas -->
</div>
<div class="content">
<!-- Text content -->
</div>
</div>
- The overlay’s position is absolute, while ensuring the parent element isn’t
position: static
, so absolute positioning works. This allows internal highlight elements to be positioned anywhere. - To place above text, simply put the overlay before the text container in HTML. The overlay height is 0 to avoid affecting interaction with text below.
- Add elements inside, set
background-color
to semi-transparent highlight color, position to highlighted text location, and set width. - The Monaco editor (VSCode is based on it) uses this method. See the official demo. After selecting a keyword, the same keywords are highlighted. Looking at the HTML, you’ll find these keywords haven’t changed styles, but there’s an Overlay on the editor interface with semi-transparent highlight colors drawn inside (see the div with
class="view-overlays"
).
SVG as Overlay
- Set overlay to same dimensions as the page
- Add
<rect>
inside, then draw at highlighted text positions - foliate-js uses this method. See overlayer.js.
Using Selection API and ::selection Pseudo-element — Browser’s Default Text Selection Highlighting
Browsers naturally highlight selected text, so controlling the selection can indirectly achieve highlighting. Styles can be customized through CSS’s ::selection
pseudo-element.
const range = new Range();
range.setStart(parentNode, startOffset);
range.setEnd(parentNode, endOffset);
// Set selection
const selection = document.getSelection();
selection.removeAllRanges();
selection.addRange(range);
::selection {
background-color: #c3c2c2;
color: white;
}
This method is quite clever, simple, and has broad browser support, but has a major flaw: since only one selection can exist at a time, programmatic selections and actual user selections will conflict and override each other. So it’s only suitable for scenarios that meet the following conditions:
- Users cannot select text
- There’s only one continuous highlighted text segment
- Need to support older browsers
- You want to be lazy and avoid other methods