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

发布于 2024-09-09 16:56:21 字数 563 浏览 6 评论 0原文

我想在页面上突出显示搜索词,但不要弄乱任何 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', $('', { 'class': 'highlight' }))
  3. 有没有办法做到简单的字符串替换而不是正则表达式?
  4. 还有其他更好/更快的方法吗?

非常感谢!

I would like to highlight search terms on a page, but not mess with any HTML tags. I was thinking of something like:

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

However, $('.searchResult *').each matches all elements, not just leaf nodes. In other words, some of the elements matched have HTML inside them. So I have a few questions:

  1. How can I match only leaf nodes?
  2. Is there some built-in jQuery RegEx function to simplify things? Something like: $(this).wrap('term', $('<span />', { 'class': 'highlight' }))
  3. Is there a way to do a simple string replace and not a RegEx?
  4. Any other better/faster way of doing this?

Thanks so much!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

碍人泪离人颜 2024-09-16 16:56:21

[查看实际操作]

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

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);
  });
}

[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, "\\
amp;");
}

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);
  });
}
痴情 2024-09-16 16:56:21

使用 contents()1, < super>23 获取包括文本节点在内的所有节点,过滤掉非文本节点,最后使用正则表达式替换每个剩余文本节点的 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();
    });
}

我们现在无法轻松地将其变成一行且更短,所以我更喜欢这样:)

请参阅此处的示例

Use contents()1, 2, 3 to get all nodes including text nodes, filter out the non-text nodes, and finally replace the nodeValue of each remaining text node using regex. This would keep the html nodes intact, and only modify the text nodes. You have to use regex instead of simple string substitutions as unfortunately we cannot do global replacements when the search term is a string.

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();
    });
}

We can't make it a one-liner and much shorter easily now, so I prefer it like this :)

See example here.

红颜悴 2024-09-16 16:56:21

我会给出突出显示 jQuery 插件一击。

I'd give the Highlight jQuery plugin a shot.

难以启齿的温柔 2024-09-16 16:56:21

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

页内荧光笔的 GitHub Page

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);
            }
        }

    }
}

I've made a pure JavaScript version of this, and packaged it into a Google Chrome plug-in, which I wish to be helpful to some people. The core function is shown below:

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);
            }
        }

    }
}
我一向站在原地 2024-09-16 16:56:21

我花了几个小时在网上搜索可以在用户输入时突出显示搜索词的代码,但没有人可以做我想做的事,直到我将一堆东西组合在一起来做到这一点(jsfiddle 演示在这里):

$.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.
}

I spent hours searching the web for code that could highlight search terms as the user types, and none could do what I wanted until I combined a bunch of stuff together to do this (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.
}
绮筵 2024-09-16 16:56:21

这是一个简单的实现,它只在 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>

Here's a naive implementation that just blasts in HTML for any match:

<!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>
你的呼吸 2024-09-16 16:56:21

我的声誉不够高,无法发表评论或添加更多链接,因此很抱歉在没有所有参考文献的情况下编写一个新答案。

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

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 转义(请参阅 stackoverflow 中“escape-string-for-use-in-javascript-regex”中 CoolAJ86 的答案)。
另一点是防止第二个“new RegExp()”,因为 RegExp.test 函数应该忽略全局标志并返回第一个匹配(请参阅 RegExp.test 上的 javascript 参考)。

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

我还将我的计时器添加到了评分最高的 galambalazs 解决方案中,您可以在 jsFiddle 中找到它。但这个的运行时间为 60-100 毫秒。

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

My reputation is not high enough for a comment or adding more links, so I am sorry to write a new answer without all references.

I was interested in the performance of the mentioned solutions above and added some code for measurement. To keep it simple I added only these lines:

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

I have forked the solution of Anurag with these lines and this resulted in 40-60ms in average.

So I forked this fiddle and made some improvements to fit my needs. One thing was the RegEx-escaping (plz see the answer from CoolAJ86 in "escape-string-for-use-in-javascript-regex" in stackoverflow).
Another point was the prevention of a second 'new RegExp()', as the RegExp.test-function should ignore the global flag and return on the first matching (plz see javascript reference on RegExp.test).

On my machine (chromium, linux) I have runtimes about 30-50ms. You can test this by yourself in this jsfiddle.

I also added my timers to the highest rated solution of galambalazs, you can find this in this jsFiddle. But this one has runtimes of 60-100ms.

The values in milliseconds become even higher and of much more importance when running (e.g. in Firefox about a quarter of a second).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文