web-dev-qa-db-fra.com

Insertion de HTML arbitraire dans un DocumentFragment

Je sais que en ajoutant innerHTML aux fragments de document a récemment été discuté et que nous espérons l'inclure dans la norme DOM Mais, quelle est la solution de contournement que vous êtes censé utiliser entre-temps?

C'est, prenez

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

Je veux à la fois la div et la span à l'intérieur de frag, avec une ligne simple.

Points bonus pour pas de boucles. jQuery est autorisé, mais j'ai déjà essayé $(html).appendTo(frag); frag est toujours vide par la suite.

50
Domenic

Voici un moyen dans les navigateurs modernes sans bouclage:

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

var frag = temp.content;

ou, en tant que réutilisable

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

UPDATE: J'ai trouvé un moyen plus simple d'utiliser l'idée principale de Pete, qui ajoute IE11 au mélange:

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

La couverture est meilleure que la méthode <template> et a été testée dans IE11, Ch, FF.

Live test/démo disponible http://pagedemos.com/str2fragment/

75
dandavis

Actuellement, la seule façon de remplir un fragment de document en utilisant uniquement une chaîne est de créer un objet temporaire et de parcourir les enfants pour les ajouter au fragment.

  • Comme il n'est pas ajouté au document, rien n'est rendu, donc les performances ne sont pas affectées.
  • Vous voyez une boucle, mais elle ne fait que parcourir les premiers enfants. La plupart des documents ne contiennent que quelques éléments semi-racine, ce qui n’a rien de grave.

Si vous souhaitez créer un document entier, utilisez plutôt DOMParser. Regardez cette réponse .

Code:

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

Une ligne (deux lignes pour la lisibilité) (entrée: String html, sortie: DocumentFragment frag):

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

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

Notez que les principales différences entre cette approche et l'approche <template> sont les suivantes:

  • Range.createContextualFragment est un peu plus largement supporté (IE11 vient de le recevoir, Safari, Chrome et FF le sont depuis un moment).

  • Les éléments personnalisés au sein du code HTML seront immédiatement mis à niveau avec la plage, mais uniquement lorsqu'ils sont clonés dans le document réel avec modèle. L’approche par modèle est un peu plus «inerte», ce qui peut être souhaitable.

8
Pete Blois

@PAEz a souligné que l'approche de @ RobW n'inclut pas text between elements. En effet, children ne récupère que Eléments , et non Nœuds . Une approche plus robuste pourrait être la suivante:

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

Les performances peuvent pâtir de l'utilisation de gros morceaux de HTML, mais il est compatible avec la plupart des navigateurs plus anciens et concis.

5
Matt

createDocumentFragment crée un "conteneur" DOM ​​vide. innerHtml et d'autres méthodes ne fonctionnent que sur les nœuds DOM (pas le conteneur). Vous devez donc d'abord créer vos nœuds, puis les ajouter au fragment. Vous pouvez le faire en utilisant une méthode pénible d’appendChild ou vous pouvez créer un nœud, le modifier innerHtml et l’ajouter à votre fragment.

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

avec jQuery, vous conservez simplement et construisez votre code HTML en tant que chaîne. Si vous voulez le convertir en un objet jQuery pour y effectuer des opérations similaires, faites simplement $ (html) qui crée un objet JQuery en mémoire. Une fois que vous êtes prêt à l'ajouter, vous l'ajoutez simplement à un élément existant d'une page.

2
Michal

Voici une solution x-browser, testée sur IE10, IE11, Edge, Chrome et 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;
        }
    }
1
bnieland

Comme @dandavis a dit, il existe une méthode standard en utilisant le template-tag.
Mais si vous aimez supporter IE11 et que vous devez analyser des éléments de table tels que '<td> test', vous pouvez utiliser cette fonction:

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
Tobias Buschor
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

Pour ce faire avec le moins de lignes possible, vous pouvez envelopper votre contenu ci-dessus dans une autre division afin de ne pas avoir à faire une boucle ou à appeler appendchild plus d'une fois. En utilisant jQuery (comme vous l’avez indiqué, c’est autorisé), vous pouvez très rapidement créer un nœud dom non attaché et le placer dans le fragment.

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