Выделите условия поиска (выберите только конечные узлы) - PullRequest
4 голосов
/ 14 июля 2010

Я хотел бы выделить поисковые термины на странице, но не связываться с какими-либо тегами 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. Любой другой лучший / быстрый способ сделать это?

Большое спасибо!

Ответы [ 7 ]

7 голосов
/ 14 июля 2010

[ увидеть его в действии ]

// 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);
  });
}
3 голосов
/ 14 июля 2010

Используйте 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();
    });
}

Мы не можем сделать его однострочным и намного короче,поэтому я предпочитаю это так:)

См. пример здесь .

2 голосов
/ 14 июля 2010

Я бы сделал плагин Highlight jQuery.

1 голос
/ 11 сентября 2013

Я сделал эту версию на чистом JavaScript и упаковал ее в плагин Google Chrome, который я хотел бы помочь некоторым людям. Основная функция показана ниже:

Страница GitHub для выделения на странице

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

    }
}
1 голос
/ 05 марта 2013

Я часами искал в Интернете код, который мог бы выделять поисковые термины как пользовательские, и никто не мог делать то, что я хотел, пока я не собрал кучу всего вместе, чтобы сделать это ( демонстрация 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.
}
0 голосов
/ 18 декабря 2013

Моя репутация недостаточно высока для комментария или добавления дополнительных ссылок, поэтому мне жаль писать новый ответ без всех ссылок.

Меня заинтересовала производительностьупомянутые выше решения и добавили некоторый код для измерения.Чтобы было проще, я добавил только эти строки:

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 (см. Ответ CoolAJ86 в «escape-string-for-use-in-javascript-regex» в stackoverflow).Другим пунктом было предотвращение второго «нового RegExp ()», так как функция RegExp.test должна игнорировать глобальный флаг и возвращаться при первом совпадении (см. Ссылку на javascript на RegExp.test).

На моей машине (Chromium, Linux) я имею время работы около 30-50 мс.Вы можете проверить это самостоятельно в этом jsfiddle .

Я также добавил свои таймеры к решению галамбалаз с наивысшим рейтингом, это можно найти в этом jsFiddle .Но у этого есть время выполнения 60-100 мс.

Значения в миллисекундах становятся еще выше и имеют гораздо большее значение при работе (например, в Firefox около четверти секунды).

0 голосов
/ 14 июля 2010

Вот наивная реализация, которая просто взрывается в 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>
...