突出显示搜索词(仅选择叶节点)

时间:2010-07-13 20:21:22

标签: jquery highlighting

我想在页面上突出显示搜索字词,但不要弄乱任何HTML标记。我想的是:

$('.searchResult *').each(function() {
    $(this.html($(this).html().replace(new RegExp('(term)', 'gi'), '<span class="highlight">$1</span>'));
)};

但是,$('.searchResult *').each匹配所有元素,而不仅仅是叶节点。换句话说,匹配的一些元素在其中包含HTML。所以我有几个问题:

  1. 如何只匹配叶节点?
  2. 是否有一些内置的jQuery RegEx函数来简化操作?类似于:$(this).wrap('term', $('<span />', { 'class': 'highlight' }))
  3. 有没有办法做一个简单的字符串替换而不是RegEx?
  4. 还有其他更好/更快的方法吗?
  5. 非常感谢!

7 个答案:

答案 0 :(得分:7)

[See it in action]

// escape by Colin Snover
// Note: if you don't care for (), you can remove it..
RegExp.escape = function(text) {
    return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
}

function highlight(term, base) {
  if (!term) return;
  base = base || document.body;
  var re = new RegExp("(" + RegExp.escape(term) + ")", "gi"); //... just use term
  var replacement = "<span class='highlight'>" + term + "</span>";
  $("*", base).contents().each( function(i, el) {
    if (el.nodeType === 3) {
      var data = el.data;
      if (data = data.replace(re, replacement)) {
        var wrapper = $("<span>").html(data);
        $(el).before(wrapper.contents()).remove();
      }
    }
  });
}

function dehighlight(term, base) {
  var text = document.createTextNode(term);
  $('span.highlight', base).each(function () {
    this.parentNode.replaceChild(text.cloneNode(false), this);
  });
}

答案 1 :(得分:3)

使用contents() 1 2 3 获取所有节点包括文本节点,过滤掉非文本节点,最后使用正则表达式替换每个剩余文本节点的nodeValue。这将保持html节点的完整性,并且只修改文本节点。您必须使用正则表达式而不是简单的字符串替换,但遗憾的是,当搜索词是字符串时,我们无法进行全局替换。

function highlight(term) {
    var regex = new RegExp("(" + term + ")", "gi");
    var localRegex = new RegExp("(" + term + ")", "i");
    var replace = '<span class="highlight">$1</span>';

    $('body *').contents().each(function() {
        // skip all non-text nodes, and text nodes that don't contain term
        if(this.nodeType != 3 || !localRegex.test(this.nodeValue)) {
            return;
        }
        // replace text node with new node(s)
        var wrapped = $('<div>').append(this.nodeValue.replace(regex, replace));
        $(this).before(wrapped.contents()).remove();
    });
}

我们现在不能轻易做到单线并且更短,所以我更喜欢这样:)

See example here

答案 2 :(得分:2)

我会给Highlight jQuery插件一个镜头。

答案 3 :(得分:1)

我花了几个小时在网上搜索可以突出显示用户类型的搜索字词的代码,没有人可以做我想要的事情,直到我把一堆东西组合起来做这个(jsfiddle demo here):

$.fn.replaceText = function(search, replace, text_only) {
    //http://stackoverflow.com/a/13918483/470749
    return this.each(function(){  
        var v1, v2, rem = [];
        $(this).find("*").andSelf().contents().each(function(){
            if(this.nodeType === 3) {
                v1 = this.nodeValue;
                v2 = v1.replace(search, replace);
                if(v1 != v2) {
                    if(!text_only && /<.*>/.test(v2)) {  
                        $(this).before( v2 );  
                        rem.push(this);  
                    } else {
                        this.nodeValue = v2;  
                    }
                }
            }
        });
        if(rem.length) {
            $(rem).remove();
        }
    });
};

function replaceParentsWithChildren(parentElements){
    parentElements.each(function() {
        var parent = this;
        var grandparent = parent.parentNode;
        $(parent).replaceWith(parent.childNodes);
        grandparent.normalize();//merge adjacent text nodes
    });
}

function highlightQuery(query, highlightClass, targetSelector, selectorToExclude){
    replaceParentsWithChildren($('.' + highlightClass));//Remove old highlight wrappers.
    $(targetSelector).replaceText(new RegExp(query, "gi"), function(match) {
        return '<span class="' + highlightClass + '">' + match + "</span>";
    }, false);
    replaceParentsWithChildren($(selectorToExclude + ' .' + highlightClass));//Do not highlight children of this selector.
}

答案 4 :(得分:1)

我已经制作了纯JavaScript版本,并将其打包到Google Chrome插件中,我希望对某些人有所帮助。核心功能如下所示:

GitHub Page for In-page Highlighter

function highlight(term){
    if(!term){
        return false;
    }

    //use treeWalker to find all text nodes that match selection
    //supported by Chrome(1.0+)
    //see more at https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker
    var treeWalker = document.createTreeWalker(
        document.body,
        NodeFilter.SHOW_TEXT,
        null,
        false
        );
    var node = null;
    var matches = [];
    while(node = treeWalker.nextNode()){
        if(node.nodeType === 3 && node.data.indexOf(term) !== -1){
            matches.push(node);
        }
    }

    //deal with those matched text nodes
    for(var i=0; i<matches.length; i++){
        node = matches[i];
        //empty the parent node
        var parent = node.parentNode;
        if(!parent){
            parent = node;
            parent.nodeValue = '';
        }
        //prevent duplicate highlighting
        else if(parent.className == "highlight"){
            continue;
        }
        else{
            while(parent && parent.firstChild){
                parent.removeChild(parent.firstChild);
            }
        }

        //find every occurance using split function
        var parts = node.data.split(new RegExp('('+term+')'));
        for(var j=0; j<parts.length; j++){
            var part = parts[j];
            //continue if it's empty
            if(!part){
                continue;
            }
            //create new element node to wrap selection
            else if(part == term){
                var newNode = document.createElement("span");
                newNode.className = "highlight";
                newNode.innerText = part;
                parent.appendChild(newNode);
            }
            //create new text node to place remaining text
            else{
                var newTextNode = document.createTextNode(part);
                parent.appendChild(newTextNode);
            }
        }

    }
}

答案 5 :(得分:0)

这是一个天真的实现,只是在任何匹配的HTML中爆炸:

<!DOCTYPE html>
<html lang"en">
<head>
    <title>Select Me</title>
    <style>
        .highlight {
            background:#FF0;
        }
    </style>
    <script type="text/javascript" src="http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js"></script>
    <script type="text/javascript">

        $(function () {

            hightlightKeyword('adipisicing');

        });

        function hightlightKeyword(keyword) {

            var replacement = '<span class="highlight">' + keyword + '</span>';
            var search = new RegExp(keyword, "gi");
            var newHtml = $('body').html().replace(search, replacement);
            $('body').html(newHtml);
        }

    </script>
</head>
<body>
    <div>

        <p>Lorem ipsum dolor sit amet, consectetur <b>adipisicing</b> elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
        <p>Lorem ipsum dolor sit amet, <em>consectetur adipisicing elit</em>, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
        <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>

    </div>
</body>
</html>

答案 6 :(得分:0)

我的声誉不足以发表评论或添加更多链接,所以很抱歉在没有所有引用的情况下撰写新答案。

我对上述解决方案的性能感兴趣,并添加了一些测量代码。为了简单起见,我只添加了以下几行:

var start = new Date();
// hightlighting code goes here ...
var end = new Date();
var ms = end.getTime() - start.getTime();
jQuery("#time-ms").text(ms);

我已经用这些线分叉了Anurag的解决方案,这导致了平均40-60毫秒。

所以我分叉了这个小提琴并做了一些改进以满足我的需求。有一件事是RegEx-escaping(请参阅CoolAJ86在stackoverflow中的“escape-string-for-use-in-javascript-regex”中的答案)。 另一点是防止第二个'新的RegExp()',因为RegExp.test函数应该忽略全局标志并返回第一个匹配(plz参见RegExp.test上的javascript参考)。

在我的机器上(铬,linux)我的运行时间约为30-50毫秒。您可以在此jsfiddle中自行测试。

我还将我的计时器添加到了最高级别的galambalaz解决方案中,你可以在jsFiddle中找到它。但是这个运行时间为60-100ms。

以毫秒为单位的值变得更高,并且在运行时更加重要(例如在Firefox中大约四分之一秒)。