使用 JavaScript 将 HTML 插入文本节点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16662393/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 05:33:03  来源:igfitidea点击:

Insert HTML into text node with JavaScript

javascriptdomtextnode

提问by alt

I've got a little text node:

我有一个小文本节点:

var node

And I want to wrap a span around every occurrence of "lol".

我想在每次出现的“lol”周围环绕一个跨度。

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

It it prints out "<span>lol<span>"when I want "lol"as a span element.

"<span>lol<span>"当我想要"lol"作为跨度元素时,它会打印出来。

采纳答案by anomaaly

You may need nodeto be the parent node, that way you can just use innerHTML:

您可能需要node成为父节点,这样您就可以使用innerHTML:

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

Here node.childNodes[0]refers to the actual text node, and nodeis its containing element.

这里node.childNodes[0]指的是实际的文本节点,node是它的包含元素。

回答by d'Artagnan Evergreen Barbosa

The answer presented by Andreas Josas is quite good. However the code had several bugs when the search term appeared several times in the same text node. Here is the solution with those bugs fixed and additionally the insert is factored up into matchText for easier use and understanding. Now only the new tag is constructed in the callback and passed back to matchText by a return.

Andreas Josas 给出的答案非常好。然而,当搜索词在同一个文本节点中出现多次时,代码有几个错误。这是修复了这些错误的解决方案,此外,为了更容易使用和理解,插入被分解到 matchText 中。现在只有新标签在回调中构造并通过返回传回 matchText。

Updated matchText function with bug fixes:

更新了带有错误修复的 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;
};

Usage:

用法:

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

If you desire to insert anchor (link) tags instead of span tags, change the create element to be "a" instead of "span", add a line to add the href attribute to the tag, and add 'a' to the excludeElements list so that links will not be created inside links.

如果您希望插入锚(链接)标签而不是跨度标签,请将创建元素更改为“a”而不是“span”,添加一行以将 href 属性添加到标签,并将“a”添加到 excludeElements列表,以便不会在链接内创建链接。

回答by Andreas Josas

The following article gives you the code to replace text with HTML elements:

以下文章为您提供了用 HTML 元素替换文本的代码:

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

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

From the article:

从文章:

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

Usage:

用法:

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

And the explanation:

和解释:

Essentially, the right way to do it is…

  1. Iterate over all text nodes.
  2. Find the substring in text nodes.
  3. Split it at the offset.
  4. Insert a span element in between the split.

从本质上讲,正确的做法是……

  1. 迭代所有文本节点。
  2. 在文本节点中查找子字符串。
  3. 在偏移处拆分它。
  4. 在拆分之间插入一个跨度元素。

回答by BrianK

Not saying this is a better answer, but I'm posting what I did for completeness. In my case I have already looked up or determined the offsets of the text that I needed to highlight in a particular #text node. This also clarifies the steps.

并不是说这是一个更好的答案,但我发布了我为完整性所做的工作。就我而言,我已经查找或确定了需要在特定 #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);

回答by liaguridio

An up to date answer for those that are finding this question now is the following :

对于那些现在发现这个问题的人来说,最新的答案如下:

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

The idea is to insert a newly created html element (lets say var div = document.createElement('div');) before the textNodeusing :

这个想法是var div = document.createElement('div');textNodeusing之前插入一个新创建的 html 元素(可以说):

textNode.parentNode.insertBefore(div,textNode);

and then use :

然后使用:

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

then remove textNodeand divusing :

然后删除textNodediv使用:

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

The insertAdjacentHTMLdoes not destroy event listeners like innerHTMLdoes .

insertAdjacentHTML不破坏事件侦听器一样innerHTML呢。

If you want to find all text nodes that are descendants of elmthen use :

如果要查找作为其后代的所有文本节点,请elm使用:

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