web-dev-qa-db-ja.com

JavaScriptでHTMLをテキストノードに挿入する

私は小さなテキストノードを持っています:

var node

そして、「笑」のすべての出現にスパンをラップしたいと思います。

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

印刷する"<span>lol<span>"欲しいとき"lol"スパン要素として。

24
alt

親ノードとしてnodeが必要になる場合があります。これにより、innerHTMLを使用できます。

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

ここに node.childNodes[0]は実際のテキストノードを参照し、nodeはそれを含む要素です。

5
anomaaly

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

スパンタグの代わりにアンカー(リンク)タグを挿入する場合は、作成要素を「スパン」ではなく「a」に変更し、href属性をタグに追加する行を追加して、excludeElementsに「a」を追加します。リンクがリンク内に作成されないようにリストします。

次の記事は、テキストを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. 分割の間にスパン要素を挿入します。
17
Andreas Josas

これは良い答えではありませんが、私は完全を期すために私がやったことを投稿しています。私の場合、特定の#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
BrianK

現在この質問を見つけている人のための最新の答えは次のとおりです:

_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);
_
0
liaguridio