The perfect solution for highlighting keywords in HTML

The perfect solution for highlighting keywords in HTML

I recently encountered a feature while working on a project: highlighting keywords on a web page.

I thought it was a simple operation that could be accomplished with an innerHTML replace, but I encountered many problems. This article records these problems and the final perfect solution, hoping to help friends who have the same experience. If you are only interested in the results, ignore the process and skip to the results.

Common practice: regular replacement

Idea: To highlight elements, you need to extract the keywords and wrap them with tags, and then adjust the style of the tags. Use innerHTML or outHTML instead of innerText or outText.

const regex = new RegExp(keyword,"g")
element.innerHTML = element.innerHTML.replace(regex,"<b class="a">"+keyword+"</b>")
element.classList.add("highlight")

The hidden dangers of doing this are as follows:

()\
div
<div id="parent">
    <div class="test">test</div>
  </div>

The keyword parent node element uses class to perform background coloring, which pollutes the original DOM to a certain extent and may affect the re-positioning of the element. (As a plugin, it is hoped that the original DOM will be changed as little as possible)

Regular optimization 1: only process elements within tags

var formatKeyword = text.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // Escape special characters contained in keyword, such as /.
var finder = new RegExp(">.*?"++".*?<") // Extract the text in the tag to avoid misoperation of class, id, etc. element.innerHTML = element.innerHTML.replace(finder,function(matched){
        return matched.replace(text,"<br>"+text+</br>)
}) // Replace keywords in the extracted tag text

This can solve most of the problems, but the problem that still exists is that as long as there are symbols like < in the tag attributes, the matching rules will be broken and regular extraction content will be incorrect. HTML5 dataset can customize any content, so these special characters are unavoidable.

<div dataset="p>d">Replace</div>

Regular optimization 2: Clear possible affected labels

<div id="keyword">keyword</div>
  =》Replace the closing tag with a variable [replaced1] keyword [replaced2] // id="keyword" in the closing tag will not be processed =》
  [replaced1]<b>keyword</b>[replaced2]
  =》Replace the temporary variable replaced with the original tag <div id="keyword"><b>keyword</b></div>
  • This idea and source code come from here, but the problem is:
  • If [replaced1] contains keyword, an exception will occur during replacement

Most importantly, this method cannot correctly extract tags when the tag value contains <> symbols.

In short, after many attempts, regular expressions have not been able to effectively handle various situations. Then I changed my mindset and processed it through nodes instead of strings. element.childNodes can most effectively clean up the noise information within the tag.

[Perfect solution] Processing through DOM nodes

<div id="parent">
    keyword 1
  <span id="child">
    keyword 2
  </span>
 </div>

Get all child nodes through parent.childNodes. The child node can be replaced by innerText.replce(keyword,result) to get the desired highlighting effect, as follows: <span id="child"><b>keyword</b> 2</span> (recursive processing: replace when the child node does not contain child nodes).

However, keyword 1 is a text node, and you can only modify the text content, but cannot add HTML, and you cannot control its style independently. And text nodes cannot be converted into ordinary nodes, which is the most annoying thing.

Finally, here comes the focus of this article. Because of this function, I got to know text nodes seriously for the first time. From here, Text is discovered, and highlighting is achieved by cutting text nodes and replacing them.

Source code and restore highlight see source code

const reg = new RegExp(keyword.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'))
highlight = function (node,reg){
    if (node.nodeType == 3) { //Only process text nodes const match = node.data.match(new RegExp(reg));
        if (match) {
          const highlightEl = document.createElement("b");
          highlightEl.dataset.highlight="y"
          const wordNode = node.splitText(match.index)
          wordNode.splitText(match[0].length); // Cut into the first keyword and the last three Text nodes const wordNew = document.createTextNode(wordNode.data);
          highlightEl.appendChild(wordNew);//Highlight node is constructed successfully wordNode.parentNode.replaceChild(highlightEl, wordNode);//Replace the text node}
    } else if (node.nodeType == 1 && node.dataset.highlight!="y"
    ) {
        for (var i = 0; i < node.childNodes.length; i++) {
            highlight(node.childNodes[i], reg);
            i++
        }
    }  
}

Summarize

The above is the perfect solution for highlighting keywords in HTML that I introduced to you. I hope it will be helpful to you. If you have any questions, please leave me a message and I will reply to you in time. I would also like to thank everyone for their support of the 123WORDPRESS.COM website!

<<:  Deeply understand the current status of HTML5 development in mobile development

>>:  Summarize the commonly used nth-child selectors

Recommend

A brief discussion on VUE uni-app conditional coding and page layout

Table of contents Conditional compilation Page La...

Remote development with VSCode and SSH

0. Why do we need remote development? When develo...

About Vue's 4 auxiliary functions of Vuex

Table of contents 1. Auxiliary functions 2. Examp...

How to prevent computer slowdown when WIN10 has multiple databases installed

Enable the service when you need it, and disable ...

Pure CSS3 to achieve mouse over button animation Part 2

After the previous two chapters, do you have a ne...

Handwriting implementation of new in JS

Table of contents 1 Introduction to the new opera...

Detailed explanation of angular parent-child component communication

Table of contents APIs used Simple Example person...

Tutorial on installing and configuring remote login to MySQL under Ubuntu

This article shares the MySQL installation and co...

An Uncommon Error and Solution for SQL Server Full Backup

1. Error details Once when manually performing a ...

Apply provide and inject to refresh Vue page method

Table of contents Method 1: Call the function dir...

Automatic backup of MySQL database using shell script

Automatic backup of MySQL database using shell sc...

MySQL Packet for query is too large problem and solution

Problem description: Error message: Caused by: co...