2012-02-14 4 views
39

Я знаю, что adding innerHTML to document fragments был недавно обсужден, и мы надеемся увидеть включение в DOM Standard. Но каково обходное решение, которое вы должны использовать в то же время?Вставка произвольного HTML в DocumentFragment

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

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

Я хочу как div и span внутри frag, с легким однострочника.

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

+0

Как насчет: frag = html :) –

ответ

58

Вот путь в modern browsers без зацикливания:

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/

+4

Не поддерживается IE вообще, поэтому «современные браузеры» немного вводят в заблуждение. –

+1

Упрощенный, потому что он работает во всех 4 современных браузерах. –

+0

Практически, если бы это было не то, что '' тег в демо. –

0

Чтобы сделать это с минимальными строками, вы можете обернуть свой контент выше в другом 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

@RobW.Это не повод для голосования, если OP писал _ «jQuery разрешен» _ – gdoron

+0

@gdoron Основная причина, по которой я ответил, - это то, что текстовое объяснение на 100% неверно. Объект DocumentFragment может иметь произвольное количество дочерних элементов. Мой предыдущий комментарий имел в виду комментарий Моргана в ответе Михал. –

+0

@RobW это был просто игривый комментарий, потому что мы отправили в то же самое время. Я вижу, как другие считают это неуместным. Я удалил его. Я понимаю, что вы имеете в виду и в моей логике. Во время моего тестирования я сделал ошибку и основал свое объяснение. Я обновил свой ответ. Спасибо – MorganTiley

2

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 в памяти. После того, как вы готовы добавить его, вы просто присоедините его к существующему элементу на странице

+0

Да, мне не нужен дополнительный элемент контейнера. – Domenic

+0

Когда вы добавляете, вам всегда нужен контейнер «лишний» (или должен указывать цель) - быть телом или каким-либо другим тегом. Если вы не хотите, чтобы это были петли для вас – Michal

+0

Я предполагаю, что был бы способ скопировать всех детей 'holder' в' frag' за один раз (без циклов). 'appendChildren' или что-то в этом роде. Но похоже, что такой метод не существует. – Domenic

23

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

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

Если вы хотите создать целый документ, используйте вместо него DOMParser. Посмотрите на this answer.

Код:

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); 
+0

Bleh, я думаю, это единственный путь. По крайней мере, вы на самом деле ответили на вопрос и удовлетворили постановку проблемы :). Тем не менее, это раздражает, когда нужно зацикливаться; Ах хорошо. – Domenic

+0

Пропуски текста между элементами .... 'var elemQueried = document.createDocumentFragment(); var tmp = document.createElement ('body'), child; tmp.innerHTML = '

x
Bleh y'; var children = tmp.childNodes; while (children.length) { elemQueried.appendChild (дети [0]); } ' – PAEz

+0

@PAEz Отправленный ответ ниже, который заботится об этой проблеме. – Matt

8

Использование 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

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

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

Даже conciser: '.firstChild' -' while (intermediateContainer.firstChild) fragement.appendChild (intermediateContainer.firstChild); ' –

+0

Хорошее предложение. Я предполагаю, что на данный момент это вопрос стиля. Благодаря! – Matt

0
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

Это не работает (по крайней мере, в Chrome). При использовании insertAdjacentHTML внутри DocumentFragment вы получите эту ошибку: 'Uncaught DOMException: не удалось выполнить 'insertAdjacentHTML' в 'Element': элемент не имеет родителя.' Кроме того, 'addChild' не является методом, вы должны использовать 'appendChild' – KevBot

0

Как @dandavis сказал, существует стандартный способ использования шаблона-тега.
Но если вы хотите поддержать IE11, и вам необходимо разобрать элементы таблицы, как «< TD> тест», вы можете использовать эту функцию:

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

Вот решение х-браузер, протестирован на 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; 
     } 
    } 
Смежные вопросы