Вставьте HTML в текстовый узел с помощью JavaScript

У меня есть небольшой текстовый узел:

var node

И я хочу обернуть диапазон вокруг каждого вхождения «лол».

node.nodeValue = node.nodeValue.replace(/lol/, "<span>lol</span>")

Он выводит "<span>lol<span>", когда я хочу "lol" в качестве элемента span.


person alt    schedule 21.05.2013    source источник
comment
в этом случае вам придется заменить текстовый узел содержимым html   -  person Arun P Johny    schedule 21.05.2013
comment
@ArunPJohny Как мне это сделать?   -  person alt    schedule 21.05.2013
comment
@JacksonGariety — вам нужно заменить текстовый узел новым элементом диапазона DOM и текстовыми узлами или изменить свойство innerHTML его родителя. Попробуй, опубликуй, что попробуешь.   -  person RobG    schedule 21.05.2013
comment
Текстовые узлы @ArunPJohny не имеют свойства innerHTML.   -  person alt    schedule 21.05.2013
comment
@JacksonGariety, вы не можете просто так установить, возможно, вам придется исправить какой-то код, чтобы заменить текстовый узел. Можете ли вы поделиться html для текстового узла   -  person Arun P Johny    schedule 21.05.2013
comment
&lt;span&gt;lol&lt;/span&gt; должен работать, я полагаю. (Вместо этого вставьте в HTML, тогда можно будет легко попасть в текстовый узел, я так думаю).   -  person Mr_Green    schedule 21.05.2013
comment
Нажмите здесь, это может помочь.   -  person Suraj Jadhav    schedule 21.05.2013
comment
возможно, этот SO может помочь вам решить эту проблему, если вы можете использовать jquery stackoverflow.com/questions/7698673/   -  person fmodos    schedule 21.05.2013


Ответы (5)


Вам может понадобиться node в качестве родительского узла, поэтому вы можете просто использовать innerHTML:

node.innerHTML=node.childNodes[0].nodeValue.replace(/lol/, "<span>lol</span>");

Здесь node.childNodes[0] относится к фактическому текстовому узлу, а node является содержащим его элементом.

person anomaaly    schedule 21.05.2013
comment
Имейте в виду, что замена innerHTML содержащего узла разрушительна для дочерних узлов, к которым могли быть добавлены прослушиватели событий с помощью сценариев. - person ccjuju; 12.08.2014
comment
У меня это не работает (Chrome 50); визуально изменений нет. Я вижу в консоли, что node.innerHTML изменился, но пользовательский интерфейс не изменился. Кроме того, node.parentNode.innerHTML не содержит нового изменения. - person Jeff; 12.05.2016
comment
Если parent.Node = body, вероятно, есть много братьев и сестер, так как бы вы узнали, какой ребенок заменить? - person Elliott B; 11.06.2018
comment
Для будущих читателей - есть еще одна важная проблема - этот ответ превращает текстовое содержимое в html. Если в текстовом содержимом есть html-теги, то они не будут экранированы, и в лучшем случае это приведет к неожиданным результатам, в худшем — к угрозе безопасности. - person lorg; 30.09.2019

Ответ, представленный Андреасом Джосасом, довольно хорош. Однако в коде было несколько ошибок, когда поисковый запрос появлялся несколько раз в одном и том же текстовом узле. Вот решение с исправленными ошибками, и, кроме того, вставка включена в matchText для облегчения использования и понимания. Теперь в обратном вызове создается только новый тег, который возвращается обратно в matchText.

Обновлена ​​функция matchText с исправлениями ошибок:

var matchText = function(node, regex, callback, excludeElements) { 

    excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
    var child = node.firstChild;

    while (child) {
        switch (child.nodeType) {
        case 1:
            if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1)
                break;
            matchText(child, regex, callback, excludeElements);
            break;
        case 3:
            var bk = 0;
            child.data.replace(regex, function(all) {
                var args = [].slice.call(arguments),
                    offset = args[args.length - 2],
                    newTextNode = child.splitText(offset+bk), tag;
                bk -= child.data.length + all.length;

                newTextNode.data = newTextNode.data.substr(all.length);
                tag = callback.apply(window, [child].concat(args));
                child.parentNode.insertBefore(tag, newTextNode);
                child = newTextNode;
            });
            regex.lastIndex = 0;
            break;
        }

        child = child.nextSibling;
    }

    return node;
};

Использование:

matchText(document.getElementsByTagName("article")[0], new RegExp("\\b" + searchTerm + "\\b", "g"), function(node, match, offset) {
    var span = document.createElement("span");
    span.className = "search-term";
    span.textContent = match;
    return span;
});

Если вы хотите вставить теги привязки (ссылки) вместо тегов span, измените элемент create на «a» вместо «span», добавьте строку, чтобы добавить атрибут href к тегу, и добавьте «a» в excludeElements. список, чтобы ссылки не создавались внутри ссылок.

person d'Artagnan Evergreen Barbosa    schedule 27.03.2015
comment
Я добавил исправление regex.lastIndex = 0 для сброса регулярного выражения при его повторном использовании. См. stackoverflow.com/questions/1520800/ - person Jeff; 12.05.2016
comment
Когда у вас будет возможность, не могли бы вы уточнить, где происходит текст и замена? - person Fred Mcgiff; 03.05.2018
comment
searchTerm — это текст, который вы ищете. Обратный вызов, который вы предоставляете, получает узел dom, найденное совпадение и смещение того, что найдено. Затем вы делаете все, что хотите, с этим знанием в своем обратном вызове, например, заменяете его, окрашиваете узел, разделяете текст и помещаете вокруг него тег или все, что хотите, теперь, когда вы знаете точное место в dom, где был ваш searchTerm нашел. - person d'Artagnan Evergreen Barbosa; 30.12.2018

В следующей статье приведен код для замены текста элементами HTML:

http://blog.alexanderdickson.com/javascript-replacing-text

Из статьи:

var matchText = function(node, regex, callback, excludeElements) { 

    excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas']);
    var child = node.firstChild;

    do {
        switch (child.nodeType) {
        case 1:
            if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) {
                continue;
            }
            matchText(child, regex, callback, excludeElements);
            break;
        case 3:
           child.data.replace(regex, function(all) {
                var args = [].slice.call(arguments),
                    offset = args[args.length - 2],
                    newTextNode = child.splitText(offset);

                newTextNode.data = newTextNode.data.substr(all.length);
                callback.apply(window, [child].concat(args));
                child = newTextNode;
            });
            break;
        }
    } while (child = child.nextSibling);

    return node;
}

Использование:

matchText(document.getElementsByTagName("article")[0], new RegExp("\\b" + searchTerm + "\\b", "g"), function(node, match, offset) {
    var span = document.createElement("span");
    span.className = "search-term";
    span.textContent = match;
    node.parentNode.insertBefore(span, node.nextSibling); 
});

И объяснение:

По сути, правильный способ сделать это…

  1. Перебрать все текстовые узлы.
  2. Найдите подстроку в текстовых узлах.
  3. Разделите его по смещению.
  4. Вставьте элемент span между разделением.
person Andreas Josas    schedule 21.05.2013
comment
Это проблема для нескольких совпадений в текстовом узле: jsfiddle.net/vw0eok5t - person Dallas; 28.06.2018
comment
Следует добавить if (child===null) return; после var child = node.firstChild;. - person WW00WW; 29.07.2018

Не говорю, что это лучший ответ, но я публикую то, что сделал для полноты картины. В моем случае я уже искал или определил смещения текста, который мне нужно было выделить в конкретном узле #text. Это также разъясняет шаги.

//node is a #text node, startIndex is the beginning location of the text to highlight, and endIndex is the index of the character just after the text to highlight     

var parentNode = node.parentNode;

// break the node text into 3 parts: part1 - before the selected text, part2- the text to highlight, and part3 - the text after the highlight
var s = node.nodeValue;

// get the text before the highlight
var part1 = s.substring(0, startIndex);

// get the text that will be highlighted
var part2 = s.substring(startIndex, endIndex);

// get the part after the highlight
var part3 = s.substring(endIndex);

// replace the text node with the new nodes
var textNode = document.createTextNode(part1);
parentNode.replaceChild(textNode, node);

// create a span node and add it to the parent immediately after the first text node
var spanNode = document.createElement("span");
spanNode.className = "HighlightedText";
parentNode.insertBefore(spanNode, textNode.nextSibling);

// create a text node for the highlighted text and add it to the span node
textNode = document.createTextNode(part2);
spanNode.appendChild(textNode);

// create a text node for the text after the highlight and add it after the span node
textNode = document.createTextNode(part3);
parentNode.insertBefore(textNode, spanNode.nextSibling);
person BrianK    schedule 12.05.2016
comment
Спасибо за это, это был лучший ответ для меня - person romuleald; 27.01.2017
comment
Как можно использовать это для чисел? Не могли бы вы привести пример поиска и замены чисел . Например, неправильный почтовый индекс заменен почтовым индексом. Число, но, как вы можете видеть, это для текста, и я все еще пытаюсь понять, как заменить числа. - person Fred Mcgiff; 03.05.2018
comment
Когда у вас будет возможность, не могли бы вы уточнить, где происходит текст и замена? - person Fred Mcgiff; 03.05.2018

Актуальный ответ для тех, кто сейчас находит этот вопрос, заключается в следующем:

function textNodeInnerHTML(textNode,innerHTML) {
    var div = document.createElement('div');
    textNode.parentNode.insertBefore(div,textNode);
    div.insertAdjacentHTML('afterend',innerHTML);
    div.remove();
    textNode.remove();
}

Идея состоит в том, чтобы вставить вновь созданный элемент html (скажем, var div = document.createElement('div');) перед textNode, используя:

textNode.parentNode.insertBefore(div,textNode);

а затем используйте:

div.insertAdjacentHTML(
 'afterend',
 textNode.data.replace(/lol/g,`<span style="color : red">lol</span>`)
) 

затем удалите textNode и div, используя:

textNode.remove();
div.remove();

insertAdjacentHTML не уничтожает прослушиватели событий, как это делает innerHTML.

Если вы хотите найти все текстовые узлы, являющиеся потомками elm, используйте:

[...elm.querySelectorAll('*')]
.map(l => [...l.childNodes])
.flat()
.filter(l => l.nodeType === 3);
person liaguridio    schedule 30.09.2019