The CSS Custom Highlight API is a powerful tool that allows developers to style arbitrary text ranges on a webpage using JavaScript and CSS. It extends beyond traditional highlight pseudo-elements like `::selection`, `::spelling-error`, and `::grammar-error`, enabling the creation of custom highlights for text ranges without altering the DOM.
In this blog post, we’ll explore the CSS Custom Highlight API, its key concepts, and how you can use it to highlight text programmatically. By the end, you’ll have a fully functional example that highlights search results on a webpage.
Why Use the CSS Custom Highlight API?
Styling text ranges programmatically is a common requirement for applications like:
- Text editors: Highlight spelling or grammar errors.
- Code editors: Highlight syntax errors or specific keywords.
- Search tools: Highlight search terms in documents.Â
The CSS Custom Highlight API makes this process seamless by allowing developers to:
- Create arbitrary `Range` objects in JavaScript.
- Define highlights using the `Highlight` interface.
- Register highlights in the `HighlightRegistry`.
- Style highlights using the `::highlight()` pseudo-element.
Key Concepts
To use the CSS Custom Highlight API, follow these four steps:
- Create `Range` objects: Define the text ranges to style.
- Create `Highlight` objects: Associate the ranges with highlights.
- Register highlights: Use the `HighlightRegistry` (`CSS.highlights`) to register highlights.
- Style highlights: Use the `::highlight()` pseudo-element to define CSS styles.
HTML
CSS Custom Highlight API Demo
CSS Custom Highlight API Demo
Maxime debitis hic, delectus perspiciatis laborum molestiae labore,
deleniti, quam consequatur iure veniam alias voluptas nisi quo. Dolorem
eaque alias, quo vel quas repudiandae architecto deserunt quidem, sapiente
laudantium nulla.
Maiores odit molestias, necessitatibus doloremque dolor illum reprehenderit
provident nostrum laboriosam iste, tempore perferendis! Ab porro neque esse
voluptas libero necessitatibus fugiat, ex, minus atque deserunt veniam
molestiae tempora? Vitae.
Dolorum facilis voluptate eaque eius similique ducimus dignissimos assumenda
quos architecto. Doloremque deleniti non exercitationem rerum quam alias
harum, nisi obcaecati corporis temporibus vero sapiente voluptatum est
quibusdam id ipsa.
CSS
/* Define a custom highlight style */
::highlight(search-results) {
background-color: #f06;
color: white;
}
JS
const query = document.getElementById('query');
const article = document.querySelector('article');
// Find all text nodes in the article. We'll search within
// these text nodes.
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) {
allTextNodes.push(currentNode);
currentNode = treeWalker.nextNode();
}
// Listen to the input event to run the search.
query.addEventListener('input', () => {
// If the CSS Custom Highlight API is not supported,
// display a message and bail-out.
if (!CSS.highlights) {
article.textContent = 'CSS Custom Highlight API not supported.';
return;
}
// Clear the HighlightRegistry to remove the
// previous search results.
CSS.highlights.clear();
// Clean-up the search query and bail-out if
// if it's empty.
const str = query.value.trim().toLowerCase();
if (!str) {
return;
}
// Iterate over all text nodes and find matches.
const ranges = allTextNodes
.map((el) => {
return { el, text: el.textContent.toLowerCase() };
})
.map(({ text, el }) => {
const indices = [];
let startPos = 0;
while (startPos < text.length) {
const index = text.indexOf(str, startPos);
if (index === -1) break;
indices.push(index);
startPos = index + str.length;
}
// Create a range object for each instance of
// str we found in the text node.
return indices.map((index) => {
const range = new Range();
range.setStart(el, index);
range.setEnd(el, index + str.length);
return range;
});
});
// Create a Highlight object for the ranges.
const searchResultsHighlight = new Highlight(...ranges.flat());
// Register the Highlight object in the registry.
CSS.highlights.set('search-results', searchResultsHighlight);
});