使用JavaScript将HTML插入文本节点

使用JavaScript将HTML插入文本节点,javascript,dom,textnode,Javascript,Dom,Textnode,我有一个小文本节点: var node 我想在“lol”的每一次出现的时候都画上一个句号 node.nodeValue=node.nodeValue.replace(/lol/,“lol”) 当我想要“lol”作为span元素时,它会打印出“lol”。下面的文章提供了用HTML元素替换文本的代码: 从文章中: var matchText = function(node, regex, callback, excludeElements) { excludeElements ||

我有一个小文本节点:

var node
我想在“lol”的每一次出现的时候都画上一个句号

node.nodeValue=node.nodeValue.replace(/lol/,“lol”)

当我想要
“lol”
作为span元素时,它会打印出
“lol”

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

从文章中:

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); 
});
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;
});
解释如下:

基本上,正确的方法是

  • 迭代所有文本节点
  • 在文本节点中查找子字符串
  • 在偏移处拆分它
  • 在拆分之间插入一个跨度图元

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

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

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

    Andreas Josas给出的答案很好。但是,当搜索词在同一文本节点中出现多次时,代码有几个错误。以下是修复这些错误的解决方案,此外,插入内容被分解为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;
        node.parentNode.insertBefore(span, node.nextSibling); 
    });
    
    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列表中,以便不会在链接中创建链接。

    并不是说这是一个更好的答案,但我正在发布我为完整性所做的事情。在我的例子中,我已经查找或确定了需要在特定的#文本节点中高亮显示的文本的偏移量。这也澄清了步骤

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

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

    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);
    
    textNode.remove();
    div.remove();
    
    然后使用:

    div.insertAdjacentHTML(
     'afterend',
     textNode.data.replace(/lol/g,`<span style="color : red">lol</span>`)
    ) 
    
    [...elm.querySelectorAll('*')]
    .map(l => [...l.childNodes])
    .flat()
    .filter(l => l.nodeType === 3);
    
    insertAdjacentHTML
    不会像
    innerHTML
    那样销毁事件侦听器

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

    div.insertAdjacentHTML(
     'afterend',
     textNode.data.replace(/lol/g,`<span style="color : red">lol</span>`)
    ) 
    
    [...elm.querySelectorAll('*')]
    .map(l => [...l.childNodes])
    .flat()
    .filter(l => l.nodeType === 3);
    

    在这种情况下,您必须使用html重新定位文本节点content@ArunPJohny我该怎么做?@JacksonGariety您需要用新的domspan元素和文本节点替换文本节点,或者修改其父节点的innerHTML属性。试一试,发布您尝试的内容。@ArunPJohny文本节点没有innerHTMl属性。@JacksonGariety您不能这样设置它,您可能需要更正一些代码来替换文本节点。您可以共享文本节点的html吗?B警告:替换包含节点的innerHTML会破坏可能已通过脚本添加事件侦听器的子节点。这对我不起作用(Chrome 50);没有视觉变化。我可以在控制台中看到,
    node.innerHTML
    已更改,但UI没有更改。另外,
    node.parentNode.innerHTML
    不包含新的更改。如果parent.node=body,可能有很多同级,那么您如何知道要替换哪个子级呢?对于未来的读者来说,还有一个重要的问题-这个答案是将文本内容转换为html。如果文本内容中碰巧有html标记,那么它们将不会被转义,充其量这将产生意外的结果,最坏的情况下这可能是一个安全风险。我添加了一个补丁
    regex.lastIndex=0
    ,以在重用regex时重置它。请您在有机会时澄清文本和替换发生的位置添加(此处放置文本)放置(此处放置替换文本)
    searchTerm
    是您正在搜索的文本。您提供的回调将接收dom节点、找到的匹配项以及找到的内容的偏移量。然后,你可以在回调中使用这些知识做任何你想做的事情,比如替换它、给节点上色、拆分文本并在其周围放置标记,或者做任何你想做的事情,现在你已经知道在dom中找到搜索词的确切位置了。谢谢你,这是对我最好的答案。你如何将这些用于数字?你能举一个查找和替换数字的例子吗。例如,将不正确的邮政编码替换为邮政编码。数字,但正如您所看到的,这是针对文本的,我仍在尝试找出替换数字。当您有机会时,请您澄清文本和替换发生在何处添加(此处放置文本)放置(此处放置替换文本)这是文本节点内多个匹配的问题:如果(child==null)返回,则应添加
    if
    after
    var child=node.firstChild