Вставка произвольного HTML в DocumentFragment
Я знаю что добавление innerHTML
чтобы фрагменты документа были недавно обсуждены, и мы надеемся увидеть включение в стандарт DOM. Но какой обходной путь вы должны использовать в то же время?
То есть взять
var html = '<div>x</div><span>y</span>';
var frag = document.createDocumentFragment();
Я хочу как то div
и span
Внутри frag
, с легким однострочником.
Бонусные баллы за отсутствие петель. jQuery разрешен, но я уже пробовал $(html).appendTo(frag)
; frag
после этого еще пусто.
9 ответов
Вот способ в современных браузерах без зацикливания:
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;
}
ОБНОВЛЕНИЕ: я нашел более простой способ использовать основную идею Пита, которая добавляет IE11 к смеси:
function fragmentFromString(strHTML) {
return document.createRange().createContextualFragment(strHTML);
}
Покрытие лучше, чем <template>
метод и проверено нормально в IE11, Ch, FF.
Живой тест / демоверсия доступна http://pagedemos.com/str2fragment/
В настоящее время единственный способ заполнить фрагмент документа, используя только строку, - создать временный объект и выполнить цикл по дочерним элементам, чтобы добавить их к фрагменту.
- Так как он не добавлен в документ, ничего не отображается, поэтому производительность не падает.
- Вы видите цикл, но он проходит только через первых потомков. В большинстве документов есть только несколько элементов с полукорнями, так что это тоже не проблема.
Если вы хотите создать целый документ, используйте вместо этого 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);
Используйте 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 будут обновлены сразу с диапазоном, но только при клонировании в реальный документ с шаблоном. Шаблонный подход немного более "инертен", что может быть желательно.
Никто и никогда не предоставил запрошенный "простой однострочный".
Учитывая переменные...
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);
@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, однако, она совместима со многими более старыми браузерами и лаконична.
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 в памяти. Когда вы будете готовы добавить его, вы просто добавляете его к существующему элементу на странице.
Я бы выбрал что-то вроде этого...
function fragmentFromString(html) {
const range = new Range();
const template = range.createContextualFragment(html);
range.selectNode(template.firstElementChild);
return range;
}
// Append to body
// document.body.append(fragmentFromString(`<div>a</div>`).cloneContents())
Таким образом, вы сохраняете контент внутри объекта Range и получаете все необходимые методы бесплатно.
Вы можете найти список всех методов и свойств Range здесь https://developer.mozilla.org/en-US/docs/Web/API/Range.
Примечание: не забудьте использовать
detatch()
как только вы закончите с ним, чтобы избежать утечек и повысить производительность.
Как сказал @dandavis, есть стандартный способ с использованием шаблона-тега.
Но если вам нравится поддерживать IE11, и вам нужно проанализировать элементы таблицы, такие как
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;
}
2017-04-08 11:16
Вот решение для 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; } }
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);
Чтобы сделать это с как можно меньшим количеством строк, вы можете заключить содержимое выше в другой div, чтобы вам не приходилось зацикливаться или вызывать appendchild более одного раза. Используя jQuery (как вы упомянули, это разрешено) вы можете очень быстро создать неприкрепленный dom-узел и поместить его во фрагмент.
var html = '<div id="main"><div>x</div><span>y</span></div>';
var frag = document.createDocumentFragment();
frag.appendChild($(html)[0]);