在HTML中查找单词

时间:2011-09-11 18:01:47

标签: javascript regex

我试图在HTML字符串中找到给定的单词并在其周围添加一个范围。

我现在正在做的是:

function find(what:String,where:String)
{
    var regexp:RegExp=new RegExp(what,'gi');
    return where.replace(regexp,'<span>$&</span>');
}

它适用于不在HTML标记内的单词。 我想要的是忽略HTML标签内的那些。

示例:查找(“西班牙”)
输入:

The rain in <b class="spain">Spain</b> stays mainly in the <i data-test="Spain">plain</i>.


输出:

The rain in <b class="spain"><span>Spain</span></b> stays mainly in the <i data-test="Spain">plain</i>.

我怎么能实现这个目标呢?

4 个答案:

答案 0 :(得分:2)

您将无法使用正则表达式以任何可靠的方式处理HTML。相反,将HTML解析为DOM树并迭代Text节点,检查其数据是否有内容。

如果您在Web浏览器中使用JavaScript,则已经为您完成了解析。请参阅this question,例如wrap-word-in-span代码。如果你需要匹配可能分散在不同元素中的短语,那就太麻烦了。

答案 1 :(得分:2)

要考虑可以匹配的html标记和属性,您需要以这种或那种方式解析该HTML。最简单的方法是将其添加到DOM(或仅添加到新元素):

var container = document.createElement("div");
container.style.display = "none";
document.body.appendChild(container);  // this step is optional
container.innerHTML = where;

解析后,您现在可以使用DOM方法迭代节点,只查找文本节点并搜索这些节点。使用递归函数来遍历节点:

function wrapWord(el, word)
{
    var expr = new RegExp(word, "i");
    var nodes = [].slice.call(el.childNodes, 0);
    for (var i = 0; i < nodes.length; i++)
    {
        var node = nodes[i];
        if (node.nodeType == 3) // textNode
        {
            var matches = node.nodeValue.match(expr);
            if (matches)
            {
                var parts = node.nodeValue.split(expr);
                for (var n = 0; n < parts.length; n++)
                {
                    if (n)
                    {
                        var span = el.insertBefore(document.createElement("span"), node);
                        span.appendChild(document.createTextNode(matches[n - 1]));
                    }
                    if (parts[n])
                    {
                        el.insertBefore(document.createTextNode(parts[n]), node);
                    }
                }
                el.removeChild(node);
            }
        }
        else
        {
            wrapWord(node, word);
        }
    }
}

这是一个有效的演示:http://jsfiddle.net/gilly3/J8JJm/3

答案 2 :(得分:1)

function find(what:String,where:String)
{
    what = what.replace(/(\[|\\|\^|\$|\.|\||\?|\*|\+|\(|\)|\{|\})/g, "\\$1")
          .replace(/[^a-zA-Z0-9\s:;'"~[\]\{\}\-_+=(),.<>*\/!@#$%^&|\\?]/g, "(?:&[0-9A-Za-z]{3,25};|&#[0-9]{1,10};?|[^\s<])")
          .replace(/</g,"&lt;?").replace(/>/g,"&gt;?").replace(/"/g,"(?:\"|&quot;?)")
          .replace(/\s/g, "(?:\\s|&nbsp;?)");

    what = "(>[^<]*|^[^<]*)(" + what + ")";
    var regexp:RegExp=new RegExp(what,'gi');
    return where.replace(regexp,'$1<span>$2</span>');
}
  1. 第一个替换函数在RE中具有特殊含义的字符之前添加反斜杠,以防止错误或意外结果。
  2. 第二个替换函数用(?:&[0-9A-Za-z]{3,25};|&#[0-9]{1,10};?|[^\s<])替换搜索查询中每次出现的未知字符。此RE由三部分组成:首先,它尝试匹配HTML实体。其次,它尝试匹配HTML数字实体。最后,它匹配任何非空白字符(如果HTML文档的创建者没有正确编码字符)。
  3. 第三,第四和第五个替换函数用相应的HTML实体替换<>",这样搜索查询就不会搜索标记。
  4. 第六个替换函数用RE(\s|&nbsp;?)替换空格,它匹配空格字符和HTML实体。
  5. 此功能的唯一缺点是未记录的特殊字符(例如)匹配任何HTML实体/字符(遵循示例,不仅&euro;而有效匹配,还有&pound;@)。

    此建议的解决方案适合大多数情况。在复杂的情况下它可能是不准确的,这可能不比DOM迭代更糟糕(它非常容易受到内存泄漏的影响并且需要更多的计算能力)。

    当您处理通过DOM分配了事件侦听器的HTML元素时,您应该遍历所有(子)元素,并将此函数应用于每个Text节点。

答案 3 :(得分:0)

  • 纯JavaScript(基于jQuery的Sizzle.getText); Demo: http://jsfiddle.net/vol7ron/U8LLv/

    var wrapText = function ( elems,regex ) {
        var re = new RegExp(regex);
        var elem;
    
        for ( var i = 0; elems[i]; i++ ) {
            elem = elems[i];
    
            // Get the text from text nodes and CDATA nodes
            if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
                parent = elem.parentNode;
                re.lastIndex = 0;
                if(re.test(elem.nodeValue)){               
                    var span = document.createElement('span');
                    span.innerHTML = RegExp.$1;
    
                    if (RegExp.leftContext != ''){
                       parent.insertBefore(document.createTextNode(RegExp.leftContext),elem);    i++;
                    }
    
                    parent.insertBefore(span,elem);   i++;
    
                    if (RegExp.rightContext != ''){
                       parent.insertBefore(document.createTextNode(RegExp.rightContext),elem);   i++;
                    }
    
                    parent.removeChild(elem);
                }                   
    
            // Traverse everything else, except comment nodes
            } else if ( elem.nodeType !== 8 ) {
                wrapText( elem.childNodes, regex );
            }
        }
    
        return;
    };
    
    
    var obj = document.getElementById('wrapper');
    wrapText([obj],/(spain)/gi);