Вставка произвольного HTML в DocumentFragment - PullRequest
52 голосов
/ 15 февраля 2012

Я знаю, что добавление innerHTML к фрагментам документа недавно обсуждалось, и мы надеемся увидеть включение в стандарт DOM.Но какой обходной путь вы должны использовать в это время?

То есть возьмите

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

Я хочу, чтобы и div, и span внутри frag, с простым однострочником.

Бонусные баллы за отсутствие петель.jQuery разрешен, но я уже пробовал $(html).appendTo(frag);frag после этого еще пуст.

Ответы [ 10 ]

83 голосов
/ 09 августа 2014

Вот способ в современных браузерах без зацикливания:

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

UPDATE: Я нашел более простой способ использовать основную идею Пита, которая добавляет в смесь IE11:

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

Покрытие лучше, чем метод <template> и проверено нормально в IE11, Ch, FF.

Доступен живой тест / демоверсия http://pagedemos.com/str2fragment/

24 голосов
/ 15 февраля 2012

В настоящее время единственный способ заполнить фрагмент документа, используя только строку, - создать временный объект и выполнить цикл по дочерним элементам, чтобы добавить их к фрагменту.

  • Поскольку он не добавляется кдокумент не визуализируется, поэтому производительность не снижается.
  • Вы видите цикл, но он проходит только по первым дочерним элементам.В большинстве документов есть только несколько элементов с полукорнями, поэтому это тоже не проблема.

Если вы хотите создать целый документ, используйте взамен 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);
}

Однострочник (две строки для удобства чтения) (ввод: String html, вывод: DocumentFragment frag):

var frag =document.createDocumentFragment(), t=document.createElement('body'), c;
t.innerHTML = html; while(c=t.firstElementChild) frag.appendChild(c);
11 голосов
/ 10 августа 2014

Использовать 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 будут обновляться немедленно с диапазоном, но только при клонировании в настоящий документ с шаблоном. Шаблонный подход немного более «инертен», что может быть желательно.

5 голосов
/ 16 мая 2016

@ PAEz указал, что подход @ RobW не включает текст между элементами.Это потому, что children только захватывает элементы , а не узлы .Более надежный подход может выглядеть следующим образом:

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, однако она совместима со многими более старыми браузерами и лаконична.

2 голосов
/ 08 апреля 2017

Как сказал @dandavis, существует стандартный способ с использованием шаблона-тега.
Но если вам нравится поддерживать IE11 и вам нужно проанализировать элементы таблицы, такие как '

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 голосов
/ 15 февраля 2012

createDocumentFragment создает пустой DOM-контейнер. innerHtml и другие методы работают только на узлах DOM (а не на контейнере), поэтому вам сначала нужно создать свои узлы, а затем добавить их во фрагмент. Вы можете сделать это, используя болезненный метод appendChild, или вы можете создать один узел, изменить его 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, просто выполните $ (html), который создает объект jquery в памяти. Когда вы будете готовы добавить его, вы просто добавляете его к существующему элементу на странице

1 голос
/ 17 августа 2017

Вот решение для x-браузера, протестированное на IE10, IE11, Edge, Chrome и FF.

    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;
        }
    }
0 голосов
/ 27 июня 2019

Никто никогда не предоставлял запрошенную «легкую однострочку».

Учитывая переменные…

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 голосов
/ 17 февраля 2017
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 голосов
/ 15 февраля 2012

Чтобы сделать это с как можно меньшим количеством строк, вы можете обернуть свой контент выше в другой div, чтобы вам не пришлось зацикливаться или вызывать appendchild более одного раза. Используя jQuery (как вы упомянули, это разрешено) вы можете очень быстро создать неприкрепленный dom-узел и поместить его во фрагмент.

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