【问题标题】:Insert HTML into text node with JavaScript使用 JavaScript 将 HTML 插入文本节点
【发布时间】:2013-05-15 18:30:12
【问题描述】:

我有一个小文本节点:

var node

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

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

当我想将"lol" 作为跨度元素时,它会打印出"&lt;span&gt;lol&lt;span&gt;"

【问题讨论】:

  • 在这种情况下,您将不得不用 html 内容替换文本节点
  • @ArunPJohny 我该怎么做?
  • @JacksonGariety——您需要用新的 DOM span 元素和文本节点替换文本节点,或者修改其父级的 innerHTML 属性。试一试,发布你的尝试。
  • @ArunPJohny 文本节点没有 innerHTMl 属性。
  • @JacksonGariety 你不能这样设置,你可能需要修改一些代码来替换文本节点。你能分享文本节点的html吗

标签: javascript dom textnode


【解决方案1】:

以下文章为您提供了用 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 元素。

【讨论】:

  • 这是文本节点中多个匹配的问题:jsfiddle.net/vw0eok5t
  • 应该在var child = node.firstChild;之后添加if (child===null) return;
【解决方案2】:

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

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

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

【讨论】:

  • 警告替换包含节点的 innerHTML 对可能通过脚本添加了事件侦听器的子节点具有破坏性。
  • 这对我不起作用(Chrome 50);没有视觉变化。我可以在控制台中看到 node.innerHTML 发生了变化,但 UI 没有变化。此外,node.parentNode.innerHTML 不包含新更改。
  • 如果parent.Node = body,可能有很多兄弟姐妹,那你怎么知道要替换哪个孩子呢?
  • 对于未来的读者——还有一个重要的问题——这个答案是将文本内容转换为 html。如果文本内容中碰巧有html标签,那么它们就不会被转义,充其量会产生意想不到的结果,最坏的情况可能是安全风险。
【解决方案3】:

Andreas Josas 给出的答案相当不错。然而,当搜索词在同一个文本节点中出现多次时,代码有几个错误。这是修复了这些错误的解决方案,此外,插入被分解为 matchText 以便于使用和理解。现在只在回调中构造了新标签,并通过 return 传回 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;
});

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

【讨论】:

  • 我添加了一个修复 regex.lastIndex = 0 以在重用时重置正则表达式。见stackoverflow.com/questions/1520800/…
  • 当你有机会时,请你澄清一下文本和替换发生在哪里添加(在此处放置文本)放置(在此处替换文本)
  • searchTerm 是您要搜索的文本。您提供的回调接收 dom 节点、找到的匹配项以及找到的内容的偏移量。然后,您可以在回调中使用该知识做任何您想做的事情,例如替换它、为节点着色、拆分文本并在其周围放置一个标签,或者您现在知道您的 searchTerm 所在的 dom 中的确切位置。找到了。
【解决方案4】:

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

【讨论】:

  • 谢谢,这对我来说是最好的答案
  • 如何将其用于数字?你能举出查找和替换数字的例子吗?例如用邮政编码替换不正确的邮政编码。数字,但您可以看到这是文本,我仍在尝试找出替换数字。
  • 当你有机会时,请你澄清一下文本和替换发生在哪里添加(在此处放置文本)放置(在此处替换文本)
【解决方案5】:

对于那些正在寻找这个问题的人来说,一个最新的答案如下:

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

这个想法是在textNode之前插入一个新创建的html元素(比如说var div = document.createElement('div');),使用:

textNode.parentNode.insertBefore(div,textNode);

然后使用:

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

然后删除 textNodediv 使用:

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

insertAdjacentHTML 不会像 innerHTML 那样破坏事件侦听器。

如果您想查找属于elm 后代的所有文本节点,请使用:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    相关资源
    最近更新 更多