2012-02-14 16 views
39

So che lo adding innerHTML to document fragments è stato discusso di recente e si spera che venga incluso nello standard DOM. Ma qual è la soluzione che dovresti usare nel frattempo?Inserimento di HTML arbitrario in un DocumentFragment

cioè prendere

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

voglio sia il div e span all'interno di frag, con un facile one-liner.

Punti bonus per no loop. jQuery è permesso, ma ho già provato $(html).appendTo(frag); Il numero frag è ancora vuoto in seguito.

+0

ne dite: frag = html :) –

risposta

58

Qui è un modo in modern browsers senza loop:

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

var frag = temp.content; 

o, come riutilizzabile

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

UPDATE: ho trovato un modo più semplice da usare idea principale di Pete, che aggiunge IE11 al mix:

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

la copertura è migliore rispetto al metodo <template> e provato bene in IE11, Ch , FF.

Live Test/demo disponibile http://pagedemos.com/str2fragment/

+4

Non supportato da Internet Explorer, quindi "browser moderni" è un po 'fuorviante. –

+1

Venerato perché funziona in tutti e 4 i browser moderni. –

+0

Quasi upvoted, se non fosse il tag '' nella demo. –

0

Per fare ciò con il minor numero di righe possibile, è possibile avvolgere il contenuto sopra in un altro div in modo da non dover ripetere o chiamare appendchild più di una volta. Usando jQuery (come accennato è permesso) puoi creare rapidamente un nodo dom non assegnato e posizionarlo nel frammento.

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

@RobW.Questo non è un buon motivo per non votare se l'OP ha scritto _ "jQuery è permesso" _ – gdoron

+0

@gdoron Il motivo principale per cui ho svalutato la risposta è che la spiegazione testuale è sbagliata al 100%. Un oggetto 'DocumentFragment' può avere un numero arbitrario di elementi figlio. Il mio commento precedente si riferiva al commento di Morgan alla risposta di Michal. –

+0

@RobW era solo un commento scherzoso perché abbiamo pubblicato allo stesso tempo. Posso vedere come gli altri lo vedrebbero come inappropriato però. L'ho rimosso. Vedo cosa intendi anche per la mia logica. Durante i miei test ho commesso un errore e ho basato la mia spiegazione su questo. Ho aggiornato la mia risposta. Grazie – MorganTiley

2

createDocumentFragment crea un "contenitore" DOM ​​vuoto. innerHtml e altri metodi funzionano solo sui nodi DOM (non sul contenitore), quindi devi prima creare i tuoi nodi e poi aggiungerli al frammento. Puoi farlo usando un doloroso metodo di appendChild oppure puoi creare un nodo e modificarlo innerHtml e aggiungerlo al tuo frammento.

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

con jquery mantieni e costruisci il tuo html come stringa. Se vuoi convertirlo in un oggetto jquery per eseguire operazioni jQuery su di esso semplicemente fai $ (html) che crea un oggetto jquery in memoria. Una volta che sei pronto ad aggiungerlo, devi semplicemente aggiungerlo a un elemento esistente in una pagina

+0

Sì, non voglio l'elemento contenitore extra. – Domenic

+0

Quando accodamenti hai sempre bisogno di un contenitore "extra" (o dovrei dire un bersaglio) - sii un body o qualche altro tag. Se non vuoi che siano i loop per te – Michal

+0

Suppongo che speravo che ci sarebbe stato un modo per copiare tutti i figli di 'holder' in' frag' in un colpo solo (senza loop). 'appendChildren' o qualcosa del genere. Ma sembra che non esiste un tale metodo. – Domenic

23

Attualmente, l'unico modo per riempire un frammento di documento utilizzando solo una stringa è creare un oggetto temporaneo e scorrere tra i bambini per aggiungere loro al frammento.

  • Poiché non è aggiunto al documento, non viene eseguito il rendering, quindi non si verifica alcun calo di prestazioni.
  • Si vede un ciclo, ma è solo in loop attraverso i primi childs. La maggior parte dei documenti ha solo pochi elementi semi-root, quindi non è un grosso problema.

Se si desidera creare un intero documento, utilizzare invece DOMParser. Dai uno sguardo a this answer.

Codice:

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

una battuta (due linee per leggibilità) (ingresso: String html, uscita: DocumentFragment frag):

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

Bleh, immagino che questa sia l'unica strada da percorrere. Almeno in realtà hai risposto alla domanda e hai soddisfatto la dichiarazione del problema :). Eppure, è fastidioso dover fare un ciclo; Ah bene. – Domenic

+0

Manca il testo tra gli elementi .... 'var elemQueried = document.createDocumentFragment(); var tmp = document.createElement ('body'), child; tmp.innerHTML = '

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

+0

@PAEz. Ha inviato una risposta qui sotto che si occupa di questo problema. – Matt

8

Uso 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); 

nota che le principali differenze tra questo approccio e l'approccio <template> sono:

  • Range.createContextualFragment è un un po 'più ampiamente supportato (solo per IE11, Safari, Chrome e FF ne hanno avuto per un po').

  • Gli elementi personalizzati all'interno dell'HTML verranno aggiornati immediatamente con l'intervallo, ma solo se clonati nel documento reale con modello. L'approccio al template è un po 'più "inerte", che può essere desiderabile.

5

@PAEz ha sottolineato che l'approccio di @ RobW non include il testo tra elementi. Questo perché children utilizza solo elementi e non nodi. Un approccio più robusto potrebbe essere il seguente:

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

Le prestazioni possono subire in grandi blocchi di HTML, tuttavia, è compatibile con molti browser più vecchi, e conciso.

+2

Even conciser: '.firstChild' -' while (intermediateContainer.firstChild) fragement.appendChild (intermediateContainer.firstChild); ' –

+0

Buon suggerimento. Suppongo che sia una questione di stile a questo punto. Grazie! – 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

Questo non funziona (almeno in Chrome). Quando si utilizza insertAdjacentHTML all'interno di DocumentFragment, si otterrà questo errore: 'Domexception Uncaught: Impossibile eseguire 'insertAdjacentHTML' su 'Element': L'elemento non ha genitore. Inoltre,' addChild' non è un metodo, dovresti usare 'appendChild' – KevBot

0

Come ha detto @dandavis, esiste un modo standard utilizzando il tag di modello.
Ma se ti piace per sostenere IE11 e avete bisogno di analizzare gli elementi da tavolo come il '< td> test', è possibile utilizzare questa funzione:

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

Ecco una soluzione x-browser, testato su IE10, IE11 , Edge, Chrome e 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; 
     } 
    } 
Problemi correlati