如何使用JavaScript高亮文本

157

请问有谁能帮我编写一个 JavaScript 函数来在网页上高亮文本?要求是只高亮一次,而不是像搜索一样高亮所有出现的文本。


6
如果你发布函数的代码,我们可以帮助你。但如果你要求我们为你创建这样一个函数...那就不太可能了。你需要自己做些事情。开始做一些东西,当你遇到困难时再回来寻求帮助。 - Felix Kling
7
是的,我已经阅读了《如何提问》,并尝试了自己的方法,但是我遇到了困难,所以才来问。我在Android平台上工作,对JavaScript知识很少,所以无法自己完成。以前我使用过另一种JavaScript,它能够完成工作,但存在某些限制。我可能没有用正确的词语来提问,对此我感到抱歉,请不要误会。 - Ankit
1
这个插件可能会对你有兴趣:https://github.com/julmot/jmHighlight。它可以将关键词分别或作为术语进行高亮显示,可以突出显示与自定义元素和类名的匹配,并且还可以搜索变音符号。此外,它还允许您过滤要搜索匹配项的上下文。 - dude
1
请查看以下正则表达式方法... https://dev59.com/Q33aa4cB1Zd3GeqPhcHg#45519242 - user2792959
我在这里准备了一篇关于此的文章,https://exhesham.com/2017/11/20/text-highlight-manipulation-with-angular2/ - Hesham Yassin
20个回答

158

你可以使用 jQuery 的 highlight 效果

但是,如果您对原始的 JavaScript 代码感兴趣,请查看我所拥有的内容。只需将其复制并粘贴到 HTML 中,打开文件并单击“highlight”-这应该会突出显示单词“fox”。就性能而言,我认为这对于小文本和单次重复(如您指定的)已经足够了。

function highlight(text) {
  var inputText = document.getElementById("inputText");
  var innerHTML = inputText.innerHTML;
  var index = innerHTML.indexOf(text);
  if (index >= 0) { 
   innerHTML = innerHTML.substring(0,index) + "<span class='highlight'>" + innerHTML.substring(index,index+text.length) + "</span>" + innerHTML.substring(index + text.length);
   inputText.innerHTML = innerHTML;
  }
}
.highlight {
  background-color: yellow;
}
<button onclick="highlight('fox')">Highlight</button>

<div id="inputText">
  The fox went over the fence
</div>

编辑:

使用replace

我看到这个答案获得了一些关注,我想增加一点内容。 你也可以很容易地使用replace函数。

"the fox jumped over the fence".replace(/fox/,"<span>fox</span>");

或者对于多个出现次数(与问题无关,但在评论中提问),只需在替换正则表达式上添加global

"the fox jumped over the other fox".replace(/fox/g,"<span>fox</span>");

替换整个网页的HTML

要替换整个网页的HTML,您应该引用文档正文的innerHTML

document.body.innerHTML


非常感谢您的回复,但您能否告诉我如何在JavaScript本身中指定颜色? - Ankit
1
你可以用 "<span style='color: " + color + ";'>" 替换 "<span class='highlight'>",其中颜色应该是类似于 var color = "#ff0000"; 的东西。 - Yaniro
如果我想在整个页面上突出显示单词的所有出现,怎么办?@Guy Mograbi - Baqer Naqvi
8
简单的“替换”是一个不好的想法。我在这里描述了为什么:https://dev59.com/XnVD5IYBdhLWcg3wAWoO#32758672 - dude
9
这不是一个好主意,因为这将尝试突出显示HTML标签/属性等。例如,在以下情况下会发生什么: <img src="fox.jpg" /> 你会得到无效的HTML,它看起来像这样: <img src="<span class='highlight'>fox</span>.jpg" /> 这不好。 - dcporter7
显示剩余2条评论

76

这里提供的解决方案非常糟糕。

  1. 你不能使用正则表达式,因为那样会在HTML标签中搜索/突出显示。
  2. 你不能使用正则表达式,因为它不能正确处理非拉丁/英文字符。
  3. 你不能只是使用innerHTML.replace,因为当字符具有特殊的HTML记号时,这种方法不起作用,例如 &amp;代表&,&lt;代表<,&gt;代表>,&auml;代表ä,&ouml;代表ö,&uuml;代表ü,&szlig;代表ß等。

你需要做的:

循环遍历 HTML 文档,查找所有文本节点,获取 textContent,使用 indexOf 获取高亮文本的位置(如果需要不区分大小写,则可选使用 toLowerCase),将 indexof 之前的内容作为 textNode 添加,将匹配的文本添加为带有高亮效果的 span,并在其后继续处理其他文本节点(高亮字符串可能在 textContent 字符串中出现多次)。

以下是相应的代码:

var InstantSearch = {

    "highlight": function (container, highlightText)
    {
        var internalHighlighter = function (options)
        {

            var id = {
                container: "container",
                tokens: "tokens",
                all: "all",
                token: "token",
                className: "className",
                sensitiveSearch: "sensitiveSearch"
            },
            tokens = options[id.tokens],
            allClassName = options[id.all][id.className],
            allSensitiveSearch = options[id.all][id.sensitiveSearch];


            function checkAndReplace(node, tokenArr, classNameAll, sensitiveSearchAll)
            {
                var nodeVal = node.nodeValue, parentNode = node.parentNode,
                    i, j, curToken, myToken, myClassName, mySensitiveSearch,
                    finalClassName, finalSensitiveSearch,
                    foundIndex, begin, matched, end,
                    textNode, span, isFirst;

                for (i = 0, j = tokenArr.length; i < j; i++)
                {
                    curToken = tokenArr[i];
                    myToken = curToken[id.token];
                    myClassName = curToken[id.className];
                    mySensitiveSearch = curToken[id.sensitiveSearch];

                    finalClassName = (classNameAll ? myClassName + " " + classNameAll : myClassName);

                    finalSensitiveSearch = (typeof sensitiveSearchAll !== "undefined" ? sensitiveSearchAll : mySensitiveSearch);

                    isFirst = true;
                    while (true)
                    {
                        if (finalSensitiveSearch)
                            foundIndex = nodeVal.indexOf(myToken);
                        else
                            foundIndex = nodeVal.toLowerCase().indexOf(myToken.toLowerCase());

                        if (foundIndex < 0)
                        {
                            if (isFirst)
                                break;

                            if (nodeVal)
                            {
                                textNode = document.createTextNode(nodeVal);
                                parentNode.insertBefore(textNode, node);
                            } // End if (nodeVal)

                            parentNode.removeChild(node);
                            break;
                        } // End if (foundIndex < 0)

                        isFirst = false;


                        begin = nodeVal.substring(0, foundIndex);
                        matched = nodeVal.substring(foundIndex, foundIndex + myToken.length);

                        if (begin)
                        {
                            textNode = document.createTextNode(begin);
                            parentNode.insertBefore(textNode, node);
                        } // End if (begin)

                        span = document.createElement("span");
                        span.className += finalClassName;
                        span.appendChild(document.createTextNode(matched));
                        parentNode.insertBefore(span, node);

                        nodeVal = nodeVal.substring(foundIndex + myToken.length);
                    } // Whend

                } // Next i 
            }; // End Function checkAndReplace 

            function iterator(p)
            {
                if (p === null) return;

                var children = Array.prototype.slice.call(p.childNodes), i, cur;

                if (children.length)
                {
                    for (i = 0; i < children.length; i++)
                    {
                        cur = children[i];
                        if (cur.nodeType === 3)
                        {
                            checkAndReplace(cur, tokens, allClassName, allSensitiveSearch);
                        }
                        else if (cur.nodeType === 1)
                        {
                            iterator(cur);
                        }
                    }
                }
            }; // End Function iterator

            iterator(options[id.container]);
        } // End Function highlighter
        ;


        internalHighlighter(
            {
                container: container
                , all:
                    {
                        className: "highlighter"
                    }
                , tokens: [
                    {
                        token: highlightText
                        , className: "highlight"
                        , sensitiveSearch: false
                    }
                ]
            }
        ); // End Call internalHighlighter 

    } // End Function highlight

};

然后你可以像这样使用它:
function TestTextHighlighting(highlightText)
{
    var container = document.getElementById("testDocument");
    InstantSearch.highlight(container, highlightText);
}

这是一个示例HTML文档。
<!DOCTYPE html>
<html>
    <head>
        <title>Example of Text Highlight</title>
        <style type="text/css" media="screen">
            .highlight{ background: #D3E18A;}
            .light{ background-color: yellow;}
        </style>
    </head>
    <body>
        <div id="testDocument">
            This is a test
            <span> This is another test</span>
            äöüÄÖÜäöüÄÖÜ
            <span>Test123&auml;&ouml;&uuml;&Auml;&Ouml;&Uuml;</span>
        </div>
    </body>
</html>

顺便说一下,如果你在数据库中使用LIKE进行搜索,例如WHERE textField LIKE CONCAT('%', @query, '%')(其实你不应该这样做,你应该使用全文搜索或者Lucene),那么你可以用\转义每个字符,并添加一个SQL转义语句,这样你就能找到特殊字符作为LIKE表达式的结果。
例如:
WHERE textField LIKE CONCAT('%', @query, '%') ESCAPE '\'

而且@query的值不是'%completed%',而是'%\c\o\m\p\l\e\t\e\d%'

(经过测试,在SQL-Server和PostgreSQL以及其他支持ESCAPE的关系型数据库管理系统中有效)


一份修订过的手稿版本:
namespace SearchTools 
{


    export interface IToken
    {
        token: string;
        className: string;
        sensitiveSearch: boolean;
    }


    export class InstantSearch 
    {

        protected m_container: Node;
        protected m_defaultClassName: string;
        protected m_defaultCaseSensitivity: boolean;
        protected m_highlightTokens: IToken[];


        constructor(container: Node, tokens: IToken[], defaultClassName?: string, defaultCaseSensitivity?: boolean)
        {
            this.iterator = this.iterator.bind(this);
            this.checkAndReplace = this.checkAndReplace.bind(this);
            this.highlight = this.highlight.bind(this);
            this.highlightNode = this.highlightNode.bind(this);    

            this.m_container = container;
            this.m_defaultClassName = defaultClassName || "highlight";
            this.m_defaultCaseSensitivity = defaultCaseSensitivity || false;
            this.m_highlightTokens = tokens || [{
                token: "test",
                className: this.m_defaultClassName,
                sensitiveSearch: this.m_defaultCaseSensitivity
            }];
        }


        protected checkAndReplace(node: Node)
        {
            let nodeVal: string = node.nodeValue;
            let parentNode: Node = node.parentNode;
            let textNode: Text = null;

            for (let i = 0, j = this.m_highlightTokens.length; i < j; i++)
            {
                let curToken: IToken = this.m_highlightTokens[i];
                let textToHighlight: string = curToken.token;
                let highlightClassName: string = curToken.className || this.m_defaultClassName;
                let caseSensitive: boolean = curToken.sensitiveSearch || this.m_defaultCaseSensitivity;

                let isFirst: boolean = true;
                while (true)
                {
                    let foundIndex: number = caseSensitive ?
                        nodeVal.indexOf(textToHighlight)
                        : nodeVal.toLowerCase().indexOf(textToHighlight.toLowerCase());

                    if (foundIndex < 0)
                    {
                        if (isFirst)
                            break;

                        if (nodeVal)
                        {
                            textNode = document.createTextNode(nodeVal);
                            parentNode.insertBefore(textNode, node);
                        } // End if (nodeVal)

                        parentNode.removeChild(node);
                        break;
                    } // End if (foundIndex < 0)

                    isFirst = false;


                    let begin: string = nodeVal.substring(0, foundIndex);
                    let matched: string = nodeVal.substr(foundIndex, textToHighlight.length);

                    if (begin)
                    {
                        textNode = document.createTextNode(begin);
                        parentNode.insertBefore(textNode, node);
                    } // End if (begin)

                    let span: HTMLSpanElement = document.createElement("span");

                    if (!span.classList.contains(highlightClassName))
                        span.classList.add(highlightClassName);

                    span.appendChild(document.createTextNode(matched));
                    parentNode.insertBefore(span, node);

                    nodeVal = nodeVal.substring(foundIndex + textToHighlight.length);
                } // Whend

            } // Next i 

        } // End Sub checkAndReplace 


        protected iterator(p: Node)
        {
            if (p == null)
                return;

            let children: Node[] = Array.prototype.slice.call(p.childNodes);

            if (children.length)
            {
                for (let i = 0; i < children.length; i++)
                {
                    let cur: Node = children[i];

                    // https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
                    if (cur.nodeType === Node.TEXT_NODE) 
                    {
                        this.checkAndReplace(cur);
                    }
                    else if (cur.nodeType === Node.ELEMENT_NODE) 
                    {
                        this.iterator(cur);
                    }
                } // Next i 

            } // End if (children.length) 

        } // End Sub iterator


        public highlightNode(n:Node)
        {
            this.iterator(n);
        } // End Sub highlight 


        public highlight()
        {
            this.iterator(this.m_container);
        } // End Sub highlight 


    } // End Class InstantSearch 


} // End Namespace SearchTools 

使用方法:

let searchText = document.getElementById("txtSearchText");
let searchContainer = document.body; // document.getElementById("someTable");
let highlighter = new SearchTools.InstantSearch(searchContainer, [
    {
        token: "this is the text to highlight" // searchText.value,
        className: "highlight", // this is the individual highlight class
        sensitiveSearch: false
    }
]);


// highlighter.highlight(); // this would highlight in the entire table
// foreach tr - for each td2 
highlighter.highlightNode(td2); // this highlights in the second column of table

13
抱歉,但您的所有论据都不正确。 1. 您绝对可以使用 RegExp,只是不应在 HTML 值内搜索,而应在元素的文本值中进行搜索。 2. 您绝对可以使用带有变音符号字符的 RegExp,就像 mark.js 中所实现的那样。 3. HTML 标记将转换为浏览器 DOM 中的实际字符,因此您也绝对可以使用它们! - dude
1
@julmot; 对于问题1:这意味着您需要遍历每个元素,这正是我所做的。除非您不关心失去格式,否则可以在document.body.innerText中搜索,但速度会相当慢。问题3:不在DOM中,而在文本元素的innerText或textContent属性中。这再次意味着您需要遍历文本元素;我认为不能用regEx解决。对于问题2:不了解mark.js,但我会避免使用任何执行jQuery.each的东西,因为那太慢了。 - Stefan Steiger
1
@StefanSteiger 1. 那么你应该更正你的决策关系,因为它说我们根本不能使用RegExp进行搜索,这是不正确的。2. 它没有使用jQuery.each。你怎么想的?3. 这不是真的,至少在Firefox中不是。例如&auml;将被转换为实际字符,即使使用innerHTML也是如此。 - dude
@dude:哥们,刚意识到这只适用于非XML转义字符,比如ä和'。所以如果你使用<> &,它将失败...这又意味着 - 没有正则表达式(除非你想替换这些字符,并冒险在某些特殊字符上失败)。 - Stefan Steiger
2
嗨@StefanSteiger,实际上我正在使用您的解决方案。这个完美无缺。但是有一些问题,比如,如果我有一个P标签,里面有两个span标签,其中一个span包含Diploma MSBTE这样的数据,第二个span包含2012这样的数据。现在,如果我要突出显示的字符串是"Diploma MSBTE 2012"这个整个字符串,我检查了一下发现它并不起作用。如果要匹配的所有内容都在一个span中,则可以工作,但如果文本内容位于不同的标记中,则无法正常工作。你能不能请告诉我一些关于这个的东西? - ganeshk
显示剩余10条评论

65

为什么使用自制高亮函数是个坏主意

自行编写高亮函数的一个坏处就是你会碰到别人已经解决过的问题。具体来说:

  • 你需要移除带有HTML元素的文本节点,才能在不破坏DOM事件和反复触发DOM重建的情况下高亮匹配项(例如使用innerHTML时)。
  • 如果你想移除高亮元素,你必须将它们的内容与HTML元素一同删除,并且还要合并分散的文本节点以进行进一步的搜索。这是必要的,因为每个高亮插件都会在文本节点内搜索匹配项,如果你的关键字被拆分成多个文本节点,它们将无法被找到。
  • 你还需要编写测试,以确保你的插件在你没有考虑到的情况下也可以正常工作。我指的是跨浏览器测试!

听起来很复杂?如果你想要一些功能,比如忽略某些元素、重音符号映射、同义词映射、在iframe内搜索、分离单词搜索等等,这变得越来越复杂。

使用现有的插件

使用现有的、良好实现的插件,你就不必担心上述问题了。Sitepoint上的10个jQuery文本高亮插件文章比较了流行的高亮插件。

看看mark.js

mark.js是一款用纯JavaScript编写的插件,但也提供作为jQuery插件的版本。它的开发旨在提供比其他插件更多的选择:

  • 分别搜索关键字而不是完整词组
  • 映射重音符号(例如,如果“justo”也应匹配“justò”)
  • 忽略自定义元素内的匹配项
  • 使用自定义高亮元素
  • 使用自定义高亮类
  • 映射自定义同义词
  • 在iframes内也进行搜索
  • 接收未找到的条款

演示

或者你可以查看这个fiddle

使用示例

// Highlight "keyword" in the specified context
$(".context").mark("keyword");

// Highlight the custom regular expression in the specified context
$(".context").markRegExp(/Lorem/gmi);

这个项目是开源的,在 GitHub 上免费提供 (项目参考链接)。

声明:我是该库的原始作者。


10
仅仅因为要突出文本而使用jQuery对我来说不足以成为一个好的理由。 - Roy
16
@Roy 我把这个放在心上了。好消息是,从v6.0.0开始,mark.js已经放弃了对jQuery的依赖,并将其作为jQuery插件可选使用。 - dude
mark.js 的第8版大小为 5.6kB,虽然无法进行树摇优化,但仍比以前要小得多。 - adi518
8
别人的图书馆仍然是“自制的”。 - Cybernetic
3
在回答中,你应明确披露自己是该库的作者。 - TheMaster
显示剩余3条评论

11

其他的解决方案都不太适合我的需求,虽然Stefan Steiger的解决方案按照我期望的工作了,但我觉得它有点啰嗦。

以下是我的尝试:

/**
 * Highlight keywords inside a DOM element
 * @param {string} elem Element to search for keywords in
 * @param {string[]} keywords Keywords to highlight
 * @param {boolean} caseSensitive Differenciate between capital and lowercase letters
 * @param {string} cls Class to apply to the highlighted keyword
 */
function highlight(elem, keywords, caseSensitive = false, cls = 'highlight') {
  const flags = caseSensitive ? 'gi' : 'g';
  // Sort longer matches first to avoid
  // highlighting keywords within keywords.
  keywords.sort((a, b) => b.length - a.length);
  Array.from(elem.childNodes).forEach(child => {
    const keywordRegex = RegExp(keywords.join('|'), flags);
    if (child.nodeType !== 3) { // not a text node
      highlight(child, keywords, caseSensitive, cls);
    } else if (keywordRegex.test(child.textContent)) {
      const frag = document.createDocumentFragment();
      let lastIdx = 0;
      child.textContent.replace(keywordRegex, (match, idx) => {
        const part = document.createTextNode(child.textContent.slice(lastIdx, idx));
        const highlighted = document.createElement('span');
        highlighted.textContent = match;
        highlighted.classList.add(cls);
        frag.appendChild(part);
        frag.appendChild(highlighted);
        lastIdx = idx + match.length;
      });
      const end = document.createTextNode(child.textContent.slice(lastIdx));
      frag.appendChild(end);
      child.parentNode.replaceChild(frag, child);
    }
  });
}

// Highlight all keywords found in the page
highlight(document.body, ['lorem', 'amet', 'autem']);
.highlight {
  background: lightpink;
}
<p>Hello world lorem ipsum dolor sit amet, consectetur adipisicing elit. Est vel accusantium totam, ipsum delectus et dignissimos mollitia!</p>
<p>
  Lorem ipsum dolor sit amet, consectetur adipisicing elit. Numquam, corporis.
  <small>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium autem voluptas perferendis dolores ducimus velit error voluptatem, qui rerum modi?</small>
</p>

如果您的关键字在正则表达式中需要转义,则我建议使用类似于 escape-string-regexp 的内容:

const keywordRegex = RegExp(keywords.map(escapeRegexp).join('|')), flags);

这对我很有效,但它还需要一种“取消标记”的方法。 - jwzumwalt
1
运行良好。谢谢。@param {boolean} caseSensitive 是相反的。 - Nadav

11
function stylizeHighlightedString() {

    var text = window.getSelection();

    // For diagnostics
    var start = text.anchorOffset;
    var end = text.focusOffset - text.anchorOffset;

    range = window.getSelection().getRangeAt(0);

    var selectionContents = range.extractContents();
    var span = document.createElement("span");

    span.appendChild(selectionContents);

    span.style.backgroundColor = "yellow";
    span.style.color = "black";

    range.insertNode(span);
}

3
Mohit,欢迎来到SO。能否提供一下代码的描述呢?这样会更好理解哦! - Nippey
难道没有一种方法可以在不创建另一个节点的情况下选择文本吗? - Dave Gregory
@user191433 这个问题不仅仅是选择文本,还需要应用样式。为此,您需要一个节点。 - Christophe
提醒/提示:JavaScript中的span.style.backgroundColor = "yellow";翻译成CSS中的style="background-color: yellow;"--驼峰式命名法和破折号表示法之间的微妙差别一开始让我困惑了。 - MarkHu
1
P.S. Mohit在https://dev59.com/fFzUa4cB1Zd3GeqP7Np3#9988468的回答是这段代码的更简化版本(例如省略了仅用于诊断/非功能的start和end变量)。 - MarkHu

10
自从HTML5以来,您可以使用<mark></mark>标签来突出显示文本。 您可以使用JavaScript在这些标记之间包装一些文本/关键字。 这里是一个小例子,演示如何标记和取消标记文本。 JSFIDDLE DEMO

1
innerHTML 是危险的。它会删除事件。 - dude
2
这也不能正常工作,因为例如,如果您在JSFIDDLE中输入“Lorem”,它只会标记第一个实例。 - agm1984
1
你只需要替换关键字的所有出现。这里有一个全局正则表达式的示例:http://jsfiddle.net/de5q704L/73/ - kasper Taeymans
这不是一个体面的解决方案,mark基本上只是一个带有黄色背景的div... - vdegenne

10

这是我的纯JavaScript正则表达式解决方案:

function highlight(text) {
    document.body.innerHTML = document.body.innerHTML.replace(
        new RegExp(text + '(?!([^<]+)?<)', 'gi'),
        '<b style="background-color:#ff0;font-size:100%">$&</b>'
    );
}

当我尝试突出显示的文本块包含HTML标记时,这对我非常有效。 - John Chapman
您还可以通过正则表达式管道符号调整函数以接受多个单词,例如 one|two|three - Klemen Tusar
如果文本末尾有>字符,则不会替换文本。使用(?!([^<]+)?<)修改正则表达式以使其正常工作。 - Archie Reyes
根据请求进行修改。 - Klemen Tusar
完美!这对我来说是最好的。 - marco burrometo

9

2
对于任何想要了解更多信息的人:这被称为“滚动到文本片段”,目前仅在Chrome上受支持。https://chromestatus.com/feature/4733392803332096 - GitGitBoom
https://dev59.com/3VIG5IYBdhLWcg3w8Wiu - GitGitBoom
  1. 在所有浏览器中都无法正常工作。
  2. 只在页面首次加载时有效。
  3. 只会突出显示第一个匹配项。
  4. 没有办法编辑 CSS 样式。
不过,这是个很好的发现。
- vdegenne

5

我有同样的问题,一堆文本通过xmlhttp请求传输过来。这些文本是HTML格式的。 我需要突出每一个发现。

str='<img src="brown fox.jpg" title="The brown fox" />'
    +'<p>some text containing fox.</p>'

问题在于我不需要突出标签中的文本。例如,我需要突出 fox:
现在我可以用以下方式替换:
var word="fox";
word="(\\b"+ 
    word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
        + "\\b)";
var r = new RegExp(word,"igm");
str.replace(r,"<span class='hl'>$1</span>")

回答您的问题:您可以省略正则表达式选项中的"g",只有第一次出现的内容会被替换,但这仍然是属性中的内容,并破坏了图像标签。
<img src="brown <span class='hl'>fox</span>.jpg" title="The brown <span 
class='hl'>fox</span> />

这是我解决问题的方法,但我想知道是否有更好的方法,在正则表达式中可能有我忽略的东西:

str='<img src="brown fox.jpg" title="The brown fox" />'
    +'<p>some text containing fox.</p>'
var word="fox";
word="(\\b"+ 
    word.replace(/([{}()[\]\\.?*+^$|=!:~-])/g, "\\$1")
    + "\\b)";
var r = new RegExp(word,"igm");
str.replace(/(>[^<]+<)/igm,function(a){
    return a.replace(r,"<span class='hl'>$1</span>");
});

这是唯一一个正则表达式解决方案,可以在不干扰<img src="word"><a href="word">的情况下为我工作。 - yvesmancera
1
黄金法则: 永远不要使用正则表达式来操纵XML。 - ScottMcGready

5
快进到2019年,Web API现在原生支持文本高亮:
const selection = document.getSelection();
selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);

接下来您只需要这样做即可!anchorNode 是选区起始节点,focusNode 是选区结束节点。如果它们是文本节点,则 offset 即为相应节点中开始和结束字符的索引。 这里是文档

他们甚至有一个实时演示


哦,这太棒了。只需像这样使用:selection.setBaseAndExtent(desiredNode, 0, desiredNode, 1); 就可以突出显示您需要的唯一节点。而且它适用于 Gutenberg。 - tonyAndr
不错,但仍然被标记为实验性的。 - adi518

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接