web-dev-qa-db-ja.com

DocumentFragmentに任意のHTMLを挿入する

innerHTMLをドキュメントフラグメントに追加する が最近議論されたことを知っており、DOM標準に含まれることを期待しています。しかし、その間に使用することになっている回避策は何ですか?

つまり、取る

_var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
_

divの中にspanfragの両方が必要で、簡単な1行のライナーが必要です。

ループなしのボーナスポイント。 jQueryは許可されていますが、すでに$(html).appendTo(frag);を試しました。 fragはその後も空です。

52
Domenic

モダンブラウザ でのループなしの方法を次に示します。

var temp = document.createElement('template');
temp.innerHTML = '<div>x</div><span>y</span>';

var frag = temp.content;

または、再利用可能として

function fragmentFromString(strHTML) {
    var temp = document.createElement('template');
    temp.innerHTML = strHTML;
    return temp.content;
}

更新:Peteのメインアイデアを使用するより簡単な方法を見つけました。IE11をミックスに追加します。

function fragmentFromString(strHTML) {
    return document.createRange().createContextualFragment(strHTML);
}

カバレッジは<template>メソッドよりも優れており、IE11、Ch、FFで問題なくテストされています。

ライブテスト/デモが利用可能 http://pagedemos.com/str2fragment/

83
dandavis

現在、文字列のみを使用してドキュメントフラグメントを埋める唯一の方法は、一時オブジェクトを作成し、子をループしてフラグメントに追加することです。

  • ドキュメントに追加されないため、何もレンダリングされないため、パフォーマンスに影響はありません。
  • ループが表示されますが、最初の子のみをループしています。ほとんどのドキュメントには、セミルート要素がわずかしか含まれていないため、大したことでもありません。

文書全体を作成する場合は、代わりにDOMParserを使用します。 この回答 をご覧ください。

コード:

var frag = document.createDocumentFragment(),
    tmp = document.createElement('body'), child;
tmp.innerHTML = '<div>x</div><span>y</span>';
while (child = tmp.firstElementChild) {
    frag.appendChild(child);
}

1行(読みやすいように2行)(入力:String html、出力:DocumentFragment frag):

var frag =document.createDocumentFragment(), t=document.createElement('body'), c;
t.innerHTML = html; while(c=t.firstElementChild) frag.appendChild(c);
24
Rob W

Range.createContextualFragment を使用します。

var html = '<div>x</div><span>y</span>';
var range = document.createRange();
// or whatever context the fragment is to be evaluated in.
var parseContext = document.body; 
range.selectNodeContents(parseContext);
var fragment = range.createContextualFragment(html);

このアプローチと<template>アプローチの主な違いは次のとおりです。

  • Range.createContextualFragmentはもう少し広くサポートされています(IE11はそれを取得しました、Safari、ChromeとFFはしばらくそれを持っています)。

  • HTML内のカスタム要素は、その範囲ですぐにアップグレードされますが、テンプレートを使用して実際のドキュメントに複製された場合のみです。テンプレートのアプローチはもう少し「不活性」であり、望ましい場合があります。

12
Pete Blois

@PAEzは、@ RobWのアプローチにはテキストbetween要素が含まれていないことを指摘しました。 childrenNodesではなく、Elementsのみを取得するためです。より堅牢なアプローチは次のとおりです。

var fragment = document.createDocumentFragment(),
    intermediateContainer = document.createElement('div');

intermediateContainer.innerHTML = "Wubba<div>Lubba</div>Dub<span>Dub</span>";

while (intermediateContainer.childNodes.length > 0) {
    fragment.appendChild(intermediateContainer.childNodes[0]);
}

HTMLの大きなチャンクではパフォーマンスが低下する場合がありますが、多くの古いブラウザーと互換性があり、簡潔です。

5
Matt

@dandavisが言ったように、template-tagを使用する標準的な方法があります。
ただし、IE11をサポートする必要があり、「<td> test」などのテーブル要素を解析する必要がある場合は、次の関数を使用できます。

function createFragment(html){
    var tmpl = document.createElement('template');
    tmpl.innerHTML = html;
    if (tmpl.content == void 0){ // ie11
        var fragment = document.createDocumentFragment();
        var isTableEl = /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(html);
        tmpl.innerHTML = isTableEl ? '<table>'+html : html;
        var els        = isTableEl ? tmpl.querySelector(RegExp.$1).parentNode.childNodes : tmpl.childNodes;
        while(els[0]) fragment.appendChild(els[0]);
        return fragment;
    }
    return tmpl.content;
}
2
Tobias Buschor

createDocumentFragmentは、空のDOM「コンテナ」を作成します。 innerHtmlおよびその他のメソッドは、コンテナではなくDOMノードでのみ機能するため、最初にノードを作成してから、フラグメントに追加する必要があります。 appendChildの痛みを伴うメソッドを使用してそれを行うか、1つのノードを作成してinnerHtmlを変更し、フラグメントに追加できます。

var frag = document.createDocumentFragment();
    var html = '<div>x</div><span>y</span>';
var holder = document.createElement("div")
holder.innerHTML = html
frag.appendChild(holder)

jqueryを使用すると、単にHTMLを文字列として保持および構築できます。それをjqueryオブジェクトに変換してjqueryのような操作を実行する場合は、メモリ内にjqueryオブジェクトを作成する$(html)を実行するだけです。追加する準備ができたら、ページ上の既存の要素に追加するだけです

2
Michal

IE10、IE11、Edge、ChromeおよびFFでテスト済みのx-ブラウザソリューションです。

    function HTML2DocumentFragment(markup: string) {
        if (markup.toLowerCase().trim().indexOf('<!doctype') === 0) {
            let doc = document.implementation.createHTMLDocument("");
            doc.documentElement.innerHTML = markup;
            return doc;
        } else if ('content' in document.createElement('template')) {
            // Template tag exists!
            let el = document.createElement('template');
            el.innerHTML = markup;
            return el.content;
        } else {
            // Template tag doesn't exist!
            var docfrag = document.createDocumentFragment();
            let el = document.createElement('body');
            el.innerHTML = markup;
            for (let i = 0; 0 < el.childNodes.length;) {
                docfrag.appendChild(el.childNodes[i]);
            }
            return docfrag;
        }
    }
1
bnieland
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
var e = document.createElement('i');
frag.appendChild(e);
e.insertAdjacentHTML('afterend', html);
frag.removeChild(e);
0
Guest

要求された「簡単なワンライナー」を提供した人はいませんでした。

与えられた変数…

var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();

…次の行がトリックを行います(Firefox 67.0.4で):

frag.append(...new DOMParser().parseFromString(html, "text/html").body.childNodes);
0
Patrick Dark

できるだけ少ない行でこれを行うには、上記のコンテンツを別のdivにラップして、appendchildを複数回ループしたり呼び出したりする必要がないようにします。 jQueryを使用すると(前述のとおり)、接続されていないdomノードを非常にすばやく作成し、フラグメントに配置できます。

var html = '<div id="main"><div>x</div><span>y</span></div>';
var frag = document.createDocumentFragment();
frag.appendChild($​(html)[0]);
0
Morgan T.