web-dev-qa-db-fra.com

Création d'un nouvel élément DOM à partir d'une chaîne HTML à l'aide de méthodes DOM intégrées ou d'un prototype

J'ai une chaîne HTML représentant un élément: '<li>text</li>'. J'aimerais l'ajouter à un élément du DOM (un ul dans mon cas). Comment puis-je faire cela avec les méthodes Prototype ou DOM?

(Je sais que je pourrais le faire facilement dans jQuery, mais malheureusement, nous n'utilisons pas jQuery.)

523
Omer Bokhari

Remarque: la plupart des navigateurs actuels prennent en charge les éléments HTML _<template>_, qui offrent un moyen plus fiable de transformer la création d'éléments à partir de chaînes. Voir réponse de Mark Amery ci-dessous pour plus de détails .

Pour les anciens navigateurs et noeud/jsdom : (qui ne prend pas encore en charge les éléments _<template>_ au moment de la rédaction), utilisez ce qui suit: méthode. C'est la même chose que les bibliothèques utilisent pour obtenir des éléments DOM d'une chaîne HTML ( avec un peu de travail supplémentaire pour IE pour contourner les bugs avec son implémentation de innerHTML):

_function createElementFromHTML(htmlString) {
  var div = document.createElement('div');
  div.innerHTML = htmlString.trim();

  // Change this to div.childNodes to support multiple top-level nodes
  return div.firstChild; 
}
_

Notez que contrairement aux modèles HTML, ceci ne fonctionnera pas pour certains éléments qui ne peuvent pas légalement être les enfants d'un _<div>_, tels que _<td>_ s.

Si vous utilisez déjà une bibliothèque, je vous recommande de vous en tenir à la méthode approuvée par la bibliothèque pour créer des éléments à partir de chaînes HTML:

732
Crescent Fresh

HTML 5 a introduit l’élément <template> qui peut être utilisé à cette fin (comme décrit maintenant dans les expressions WhatWG spec et MDN docs ).

Un <template> est un élément HTML auquel tout autre type d'élément est autorisé en tant qu'enfant. template a une propriété .content à laquelle vous pouvez accéder avec JavaScript qui pointe vers DocumentFragment avec le contenu du modèle. Cela signifie que vous pouvez convertir une chaîne HTML en éléments DOM en définissant le innerHTML d'un élément <template>, puis en atteignant la propriété .content de template.

Exemples:

/**
 * @param {String} HTML representing a single element
 * @return {Element}
 */
function htmlToElement(html) {
    var template = document.createElement('template');
    html = html.trim(); // Never return a text node of whitespace as the result
    template.innerHTML = html;
    return template.content.firstChild;
}

var td = htmlToElement('<td>foo</td>'),
    div = htmlToElement('<div><span>nested</span> <span>stuff</span></div>');

/**
 * @param {String} HTML representing any number of sibling elements
 * @return {NodeList} 
 */
function htmlToElements(html) {
    var template = document.createElement('template');
    template.innerHTML = html;
    return template.content.childNodes;
}

var rows = htmlToElements('<tr><td>foo</td></tr><tr><td>bar</td></tr>');

Notez que les approches similaires à tilisez un élément conteneur différent, tel que div ne fonctionnent pas tout à fait. HTML impose des restrictions sur les types d'élément autorisés à exister à l'intérieur de quels autres types d'élément; Par exemple, vous ne pouvez pas définir td comme enfant direct de div. Cela entraîne la disparition de ces éléments si vous essayez de définir le innerHTML d'un div pour les contenir. Étant donné que <template>s n'a pas de telles restrictions sur leur contenu, cette lacune ne s'applique pas à l'utilisation d'un modèle.

Cependant, template n'est pas pris en charge par certains anciens navigateurs. À partir de janvier 2018, Puis-je utiliser ... estimations 90% des utilisateurs mondiaux utilisent un navigateur prenant en charge templates . En particulier, aucune version d'Internet Explorer ne les prend en charge; Microsoft n'a pas implémenté le support template jusqu'à la sortie d'Edge.

Si vous avez la chance d'écrire du code destiné uniquement aux utilisateurs de navigateurs modernes, utilisez-le dès maintenant. Sinon, vous devrez peut-être attendre un moment pour que les utilisateurs se mettent à jour.

267
Mark Amery

Utilisez insertAdjacentHTML () . Cela fonctionne avec tous les navigateurs actuels, même avec IE11.

var mylist = document.getElementById('mylist');
mylist.insertAdjacentHTML('beforeend', '<li>third</li>');
<ul id="mylist">
 <li>first</li>
 <li>second</li>
</ul>
74

Les nouvelles implémentations DOM ont range.createContextualFragment , qui fait ce que vous voulez d'une manière indépendante de la structure.

C'est largement supporté. Pour être sûr cependant, vérifiez sa compatibilité dans le même lien MDN, car il sera modifié. À partir de mai 2017, c'est ça:

_Feature         Chrome   Edge   Firefox(Gecko)  Internet Explorer   Opera   Safari
Basic support   (Yes)    (Yes)  (Yes)           11                  15.0    9.1.2
_
26
kojiro

Pas besoin de Tweak, vous avez une API native:

const toNodes = html =>
    new DOMParser().parseFromString(html, 'text/html').body.childNodes
16
math2001

Voici un moyen simple de le faire:

String.prototype.toDOM=function(){
  var d=document
     ,i
     ,a=d.createElement("div")
     ,b=d.createDocumentFragment();
  a.innerHTML=this;
  while(i=a.firstChild)b.appendChild(i);
  return b;
};

var foo="<img src='//placekitten.com/100/100'>foo<i>bar</i>".toDOM();
document.body.appendChild(foo);
15
william malo

Pour certains fragments html tels que les solutions <td>test</td>, div.innerHTML, DOMParser.parseFromString et range.createContextualFragment (sans le contexte correct) citées dans les autres réponses ne créeront pas l'élément <td>.

jQuery.parseHTML () les gère correctement (j'ai extrait la fonction parseHTML de jQuery 2 en une fonction indépendante qui peut être utilisée dans des bases de code autres que jquery).

Si vous ne supportez que Edge 13+, il est plus simple d'utiliser simplement la balise de modèle HTML5:

function parseHTML(html) {
    var t = document.createElement('template');
    t.innerHTML = html;
    return t.content.cloneNode(true);
}

var documentFragment = parseHTML('<td>Test</td>');
12
Munawwar

Avec Prototype, vous pouvez également faire:

HTML:

<ul id="mylist"></ul>

JS:

$('mylist').insert('<li>text</li>');
10
Pablo Borowicz

Vous pouvez créer des noeuds DOM valides à partir d'une chaîne en utilisant:

document.createRange().createContextualFragment()

L'exemple suivant ajoute un élément de bouton dans la page en prenant le balisage d'une chaîne:

let html = '<button type="button">Click Me!</button>';
let fragmentFromString = function (strHTML) {
  return document.createRange().createContextualFragment(strHTML);
}
let fragment = fragmentFromString(html);
document.body.appendChild(fragment);
8
GibboK

J'utilise cette méthode (Fonctionne dans IE9 +), bien qu'elle n'analyse pas <td> ni d'autres enfants directs non valides du corps:

function stringToEl(string) {
    var parser = new DOMParser(),
        content = 'text/html',
        DOM = parser.parseFromString(string, content);

    // return element
    return DOM.body.childNodes[0];
}

stringToEl('<li>text</li>'); //OUTPUT: <li>text</li>
5
usrbowe

Pour améliorer encore l’extrait utile . ToDOM () que nous pouvons trouver à différents endroits, nous pouvons maintenant utiliser en toute sécurité (---) backticks ( littéraux du modèle ).

Ainsi, nous pouvons avoir des guillemets simples et doubles dans la déclaration foo html.

Cela se comporte comme heredocs pour ceux qui connaissent le terme.

Ceci peut être amélioré avec des variables, pour créer des templates complexes:

Les littéraux de modèle sont entourés par le caractère back-tick () (accent grave) au lieu de guillemets simples ou doubles. Les littéraux de modèle peuvent contenir des espaces réservés. Celles-ci sont indiquées par le signe dollar et les accolades ($ {expression}). Les expressions contenues dans les espaces réservés et le texte qui les sépare sont transmises à une fonction. La fonction par défaut concatène simplement les pièces en une seule chaîne. S'il existe une expression précédant le littéral de modèle (balise ici), cela s'appelle un "modèle marqué". Dans ce cas, l'expression de la balise (généralement une fonction) est appelée avec le littéral de modèle traité, que vous pouvez ensuite manipuler avant la sortie. Pour échapper à une balise arrière dans un modèle de modèle, placez une barre oblique inversée\avant la balise arrière.

String.prototype.toDOM=function(){
  var d=document,i
     ,a=d.createElement("div")
     ,b=d.createDocumentFragment()
  a.innerHTML = this
  while(i=a.firstChild)b.appendChild(i)
  return b
}

// Using template litterals
var a = 10, b = 5
var foo=`
<img 
  onclick="alert('The future start today!')"   
  src='//placekitten.com/100/100'>
foo${a + b}
  <i>bar</i>
    <hr>`.toDOM();
document.body.appendChild(foo);
img {cursor: crosshair}

Alors, pourquoi ne pas utiliser directement .innerHTML +=? Ce faisant, le navigateur recalcule l’ensemble du DOM, c’est beaucoup plus lent.

https://caniuse.com/template-literals

3
NVRM

J'ai ajouté un prototype Document qui crée un élément à partir de chaîne:

Document.prototype.createElementFromString = function (str) {
    const element = new DOMParser().parseFromString(str, 'text/html');
    const child = element.documentElement.querySelector('body').firstChild;
    return child;
};
3
Raza

En retard mais juste comme une note;

Il est possible d'ajouter un élément trivial à l'élément cible en tant que conteneur et de le supprimer après utilisation.

// Testé sur chrome 23.0, firefox 18.0, soit 7-8-9 et opera 12.11.

<div id="div"></div>

<script>
window.onload = function() {
    var foo, targetElement = document.getElementById('div')
    foo = document.createElement('foo')
    foo.innerHTML = '<a href="#" target="_self">Text of A 1.</a> '+
                    '<a href="#" onclick="return !!alert(this.innerHTML)">Text of <b>A 2</b>.</a> '+
                    '<hr size="1" />'
    // Append 'foo' element to target element
    targetElement.appendChild(foo)

    // Add event
    foo.firstChild.onclick = function() { return !!alert(this.target) }

    while (foo.firstChild) {
        // Also removes child nodes from 'foo'
        targetElement.insertBefore(foo.firstChild, foo)
    }
    // Remove 'foo' element from target element
    targetElement.removeChild(foo)
}
</script>
2
K-Gun

HTML5 et ES6

<template>

Démo

"use strict";

/**
 *
 * @author xgqfrms
 * @license MIT
 * @copyright xgqfrms
 * @description HTML5 Template
 * @augments
 * @example
 *
 */

/*

<template>
    <h2>Flower</h2>
    <img src="https://www.w3schools.com/tags/img_white_flower.jpg">
</template>


<template>
    <div class="myClass">I like: </div>
</template>

*/

const showContent = () => {
    // let temp = document.getElementsByTagName("template")[0],
    let temp = document.querySelector(`[data-tempalte="tempalte-img"]`),
        clone = temp.content.cloneNode(true);
    document.body.appendChild(clone);
};

const templateGenerator = (datas = [], debug = false) => {
    let result = ``;
    // let temp = document.getElementsByTagName("template")[1],
    let temp = document.querySelector(`[data-tempalte="tempalte-links"]`),
        item = temp.content.querySelector("div");
    for (let i = 0; i < datas.length; i++) {
        let a = document.importNode(item, true);
        a.textContent += datas[i];
        document.body.appendChild(a);
    }
    return result;
};

const arr = ["Audi", "BMW", "Ford", "Honda", "Jaguar", "Nissan"];

if (document.createElement("template").content) {
    console.log("YES! The browser supports the template element");
    templateGenerator(arr);
    setTimeout(() => {
        showContent();
    }, 0);
} else {
    console.error("No! The browser does not support the template element");
}
@charset "UTf-8";

/* test.css */

:root {
    --cololr: #000;
    --default-cololr: #fff;
    --new-cololr: #0f0;
}

[data-class="links"] {
    color: white;
    background-color: DodgerBlue;
    padding: 20px;
    text-align: center;
    margin: 10px;
}
<!DOCTYPE html>
<html lang="zh-Hans">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=Edge">
    <title>Template Test</title>
    <!--[if lt IE 9]>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.js"></script>
    <![endif]-->
</head>

<body>
    <section>
        <h1>Template Test</h1>
    </section>
    <template data-tempalte="tempalte-img">
        <h3>Flower Image</h3>
        <img src="https://www.w3schools.com/tags/img_white_flower.jpg">
    </template>
    <template data-tempalte="tempalte-links">
        <h3>links</h3>
        <div data-class="links">I like: </div>
    </template>
    <!-- js -->
</body>

</html>
1
xgqfrms-gildata

Voici mon code, et ça marche:

function parseTableHtml(s) { // s is string
    var div = document.createElement('table');
    div.innerHTML = s;

    var tr = div.getElementsByTagName('tr');
    // ...
}
1
Wen Qi

Pour le plaisir, j’ai pensé partager cette approche compliquée mais simple à laquelle j’ai abouti ... Peut-être que quelqu'un trouvera quelque chose d’utile.

/*Creates a new element - By Jamin Szczesny*/
function _new(args){
    ele = document.createElement(args.node);
    delete args.node;
    for(x in args){ 
        if(typeof ele[x]==='string'){
            ele[x] = args[x];
        }else{
            ele.setAttribute(x, args[x]);
        }
    }
    return ele;
}

/*You would 'simply' use it like this*/

$('body')[0].appendChild(_new({
    node:'div',
    id:'my-div',
    style:'position:absolute; left:100px; top:100px;'+
          'width:100px; height:100px; border:2px solid red;'+
          'cursor:pointer; background-color:HoneyDew',
    innerHTML:'My newly created div element!',
    value:'for example only',
    onclick:"alert('yay')"
}));
0
JxAxMxIxN

var msg = "test" jQuery.parseHTML (msg)

0
nitish kumar