2014-03-10 1 views
5

Я пытаюсь создать функцию текстового поиска, но мне трудно заставить ее работать, когда внутри элемента находится html. Вот несколько простых html, чтобы продемонстрировать мою проблему.Замените текст внутри элемента текстом, содержащим html, без удаления html уже присутствующего

<b> 
    <input type="checkbox" value="" /> 
    I need replaced 
</b> 

И вот где я сейчас нахожусь для javascript. Он отлично работает, полагая, что внутри нет html.

$("*", search_container).each(function() { 
    var replaceTxt = $(this).text().replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>'); 
    $(this).text().replaceWith(replaceTxt); 
}); 

Как пользователь печатает, мне нужно заменить текст на span. Таким образом, контент должен выглядеть следующим образом.

<b> 
    <input type="checkbox" value="" /> 
    <span style="color: #0095DA" class="textSearchFound">I need</span> replaced 
</b> 

UPDATE

Осмотрев вопрос, который был признан дубликат я придумал это. Который может быть близок, он вставляет html в DOM как текст, который я не хочу. Пожалуйста, проголосуйте, чтобы открыть его снова.

$("*", search_container).each(function() { 
    var node = $(this).get(0); 
    var childs = node.childNodes; 
    for(var inc = 0; inc < childs.length; inc++) { 
     //Text node 
     if(childs[inc].nodeType == 3){ 
      if(childs[inc].textContent) { 
       childs[inc].textContent = childs[inc].textContent.replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>'); 
      } 
      //IE 
      else { 
       childs[inc].nodeValue = childs[inc].nodeValue.replace(new RegExp("(" + search_term + ")", 'i'), '<span style="color: #0095DA;" class="textSearchFound">$1</span>'); 
      } 
     } 
    } 
}); 
+0

Является ли это использовать на странице вы можете контролировать HTML для, или более общий случай, когда вы хотите, чтобы иметь возможность заменить текст, и у вас нет контроля над структурой html? –

+0

@JasonAller Более общая причина. Мне нужно, чтобы это работало при любых условиях, когда я не знаю, что будет в элементах. – Metropolis

+0

Возможно, попробуйте этот плагин? http://bartaz.github.io/sandbox.js/jquery.highlight.html –

ответ

4

Это некрасиво, но лучшим способом сделать это было бы циклически перебирать каждый узел на странице. Когда вы сталкиваетесь с текстовым узлом, проверьте, содержит ли он строку поиска. Если вы его найдете, удалите исходный текстовый узел и замените его текстовым узлом текста перед совпадением, новым элементом с выделенным совпадением и текстовым узлом, что после матча.

Вот пример функции:

var highlightSomeText = function(needle, node){ 
    node = node || document.body; // initialize the first node, start at the body 

    if(node.nodeName == 'SCRIPT') return; // don't mess with script tags 

    if(node.nodeType == 3){ // if node type is text, search for our needle 
     var parts = node.nodeValue.split(needle); // split text by our needle 
     if(parts.length > 1){ // if we found our needle 
      var parent = node.parentNode 
      for(var i = 0, l = parts.length; i < l;){ 
       var newText = document.createTextNode(parts[i++]); // create a new text node, increment i 
       parent.insertBefore(newText, node); 
       if(i != l){ // if we're not on the last part, insert a new span element for our highlighted text 
        var newSpan = document.createElement('span'); 
        newSpan.className = 'textSearchFound'; 
        newSpan.style.color = '#0095DA'; 
        newSpan.innerHTML = needle; 
        parent.insertBefore(newSpan, node); 
       } 
      } 
      parent.removeChild(node); // delete the original text node 
     } 
    } 

    for(var i = node.childNodes.length; i--;) // loop through all child nodes 
     highlightSomeText(needle, node.childNodes[i]); 
}; 

highlightSomeText('I need'); 

И демо на jsfiddle: http://jsfiddle.net/8Mpqe/


Edit: Вот обновленный метод, который использует Regexp быть чувствительны к регистру: http://jsfiddle.net/RPuRG/

Обновленные линии:

var parts = node.nodeValue.split(new RegExp('(' + needle + ')','i')); 

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

newSpan.innerHTML = parts[i++]; 

Текст нового элемента span будет следующей частью нашего массива.

Редактирование 2: Поскольку эта функция вызывается в каждом событии с клавиатурой какого-либо текстового поля, то искатель хотел удалить выделенные метки диапазона перед каждым прогоном. Для этого необходимо развернуть каждый выделенный элемент, затем вызвать normalize на родительском узле, чтобы объединить соседние текстовые узлы обратно. Вот пример JQuery:

$('.textSearchFound').each(function(i, el){ 
    $(el).contents().unwrap(). 
    parent()[0].normalize(); 
}); 

А вот завершенная работает демо: http://jsfiddle.net/xrL3F/

+0

. В настоящее время я пытаюсь привести этот пример в то, что у меня есть. Похоже, вы хорошо его работали, я просто пытаюсь изменить его и заставить его работать. – Metropolis

+0

Я только что обновил его, чтобы более точно сравнить ваш оригинальный пример. Новая версия должна быть нечувствительной к регистру. Если вы хотите только сопоставить текст внутри некоторого родительского элемента, вы можете заменить 'document.body' в первой строке любым корневым элементом, который вы выберете. – nderscore

+0

Хорошо, его близко. Однако я использую событие keydown для этой функции, поэтому мне нужно просто пропустить середину без рекурсии, и мне нужно развернуть промежутки в начале функции. – Metropolis

1

Вы можете использовать метод JQuery .contents(), чтобы получить дочерние узлы элемента. Текстовые узлы будут иметь nodeType == 3. Затем вы можете получить текст текстового узла, используя его свойство textContent.

var getNonEmptyChildTextNodes = function($element) { 
    return $element.contents().filter(function() { 
     return (this.nodeType == 3) && (this.textContent.trim() != ''); 
    }); 
}; 

var $textNode = getNonEmptyChildTextNodes($('b')).first(), 
    text = $textNode[0].textContent; 

Затем выполнить замену, вы можете сделать следующее:

var search_term = 'I need', 
    regExp = new RegExp(search_term, 'gi'); 

var result = regExp.exec(text); 
if (result !== null) { 
    var $elements = $(), 
     startIndex = 0; 
    while (result !== null) { 
     $elements = $elements.add(document.createTextNode(text.slice(startIndex, result.index))); 
     $elements = $elements.add('<span style="color: #0095DA;" class="textSearchFound">' + result[0] + '</span>'); 
     startIndex = regExp.lastIndex; 
     result = regExp.exec(text); 
    } 
    $elements = $elements.add(document.createTextNode(text.slice(startIndex))); 
    $textNode.replaceWith($elements); 
} 

jsfiddle


Вот jsfiddle показывает полный поиск & обертку, используя код выше.

+0

Он не просто хочет заменить текст, он хочет вставить новый элемент, где находится соответствующий текст. – nderscore

+0

Но что, если мне нужно это дважды? http://jsfiddle.net/NarJ9/3/ И вы добавляете дополнительный тег span вокруг всего. – nderscore

+0

Добавляет дополнительную разметку, которая может изменить способ ее отображения. Я думаю, что более важно быть верным, чем просто. Регулярное выражение также можно упростить, удалив группу захвата '()' и заменив '$ 1' на' $ & ' – nderscore

Смежные вопросы