Membuat elemen DOM baru dari string HTML menggunakan metode DOM bawaan atau Prototipe


621

Saya memiliki string HTML yang mewakili unsur: '<li>text</li>'. Saya ingin menambahkannya ke elemen di DOM (a uldalam kasus saya). Bagaimana saya bisa melakukan ini dengan Prototipe atau dengan metode DOM?

(Saya tahu saya bisa melakukan ini dengan mudah di jQuery, tapi sayangnya kami tidak menggunakan jQuery.)


Maaf, sebenarnya apa yang ingin Anda capai? String HTML -> elemen dom?
Crescent Fresh

36
Sangat disayangkan bahwa solusi ini sangat tidak langsung. Saya berharap komite standar akan menetapkan sesuatu yang serupa seperti:var nodes = document.fromString("<b>Hello</b> <br>");
Sridhar Sarnobat


1
Saya memiliki masalah dengan hal di atas, karena saya memiliki atribut yang perlu untuk diteruskan dan saya tidak ingin menguraikannya: "<table id = '5ccf9305-4b10-aec6-3c55-a6d218bfb107' class = 'data-table tampilan baris-batas 'cellspacing =' 0 'width =' 100% '> </table> "jadi, saya hanya menggunakan: $ (" <table id =' 5ccf9305-4b10-aec6-3c55-a6d218bfb107 'data class' ' -tabel tampilan perbatasan-batas 'cellspacing =' 0 'width =' 100% '> </table> ")
stevenlacerda

Jawaban:


818

Catatan: sebagian besar browser saat ini mendukung <template>elemen HTML , yang menyediakan cara yang lebih dapat diandalkan untuk mengubah elemen dari string. Lihat jawaban Mark Amery di bawah ini untuk detailnya .

Untuk browser yang lebih lama, dan simpul / jsdom : (yang belum mendukung <template>elemen pada saat penulisan), gunakan metode berikut. Ini adalah hal yang sama yang digunakan perpustakaan untuk mendapatkan elemen DOM dari string HTML ( dengan beberapa pekerjaan tambahan untuk IE untuk mengatasi bug dengan penerapannya 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; 
}

Perhatikan bahwa tidak seperti templat HTML, ini tidak akan berfungsi untuk beberapa elemen yang tidak dapat secara hukum menjadi anak-anak dari <div>, seperti <td>s.

Jika Anda sudah menggunakan perpustakaan, saya sarankan Anda tetap menggunakan metode yang disetujui perpustakaan untuk membuat elemen dari string HTML:


1
Cara mengatur innerHTML ke div yang dibuat menggunakan jQuery tanpa menggunakan tugas innerHTML yang tidak aman ini (div.innerHTML = "some value")
Shivanshu Goyal

12
Nama fungsi createElementFromHTML menyesatkan karena div.firstChildmengembalikan Nodeyang bukan HTMLElementeg tidak bisa node.setAttribute. Untuk membuat Elementpengembalian div.firstElementChilddari fungsi saja.
Semmel

Terima kasih, <div>membungkus HTML yang saya tambahkan dengan .innerHTMLitu mengganggu saya. Saya tidak pernah berpikir untuk menggunakan .firstChild.
Ivan

Saya mencoba untuk mem-parsing SVG di dalam yang dibuat divdan hasilnya adalah [object SVGSVGElement]ketika log konsol memberi saya elemen DOM yang benar. Apa yang saya lakukan salah?
ed1nh0

1
Saya akan menggunakan firstElementChild daripada firstChild (lihat w3schools.com/jsref/prop_element_firstelementchild.asp ), karena jika ada ruang di depan atau akhir templat, anak pertama akan mengembalikan textNode kosong
Chris Panayotoff

343

HTML 5 memperkenalkan <template>elemen yang dapat digunakan untuk tujuan ini (seperti yang sekarang dijelaskan dalam spesifikasi WhatWG dan dokumen MDN ).

Sebuah <template>elemen digunakan untuk menyatakan fragmen HTML yang dapat dimanfaatkan dalam skrip. Elemen diwakili dalam DOM sebagai HTMLTemplateElementyang memiliki .contentproperti DocumentFragmenttipe, untuk menyediakan akses ke konten template. Ini berarti bahwa Anda dapat mengkonversi string HTML untuk elemen DOM dengan menetapkan innerHTMLsebuah <template>elemen, kemudian mencapai ke dalam template's .contentproperti.

Contoh:

/**
 * @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>');

Perhatikan bahwa pendekatan serupa yang menggunakan elemen wadah yang berbeda sepertidiv tidak cukup berhasil. HTML memiliki batasan pada jenis elemen apa yang diizinkan ada di dalam jenis elemen lainnya; misalnya, Anda tidak dapat menempatkan tdsebagai anak langsung dari div. Ini menyebabkan elemen-elemen ini menghilang jika Anda mencoba mengatur innerHTMLa divuntuk menampungnya. Karena <template>s tidak memiliki batasan seperti itu pada konten mereka, kekurangan ini tidak berlaku ketika menggunakan templat.

Namun, templatetidak didukung di beberapa browser lama. Pada Januari 2018, Dapatkah saya menggunakan ... memperkirakan 90% pengguna secara global menggunakan browser yang mendukung templates . Secara khusus, tidak ada versi Internet Explorer yang mendukungnya; Microsoft tidak menerapkan templatedukungan hingga dirilisnya Edge.

Jika Anda cukup beruntung untuk menulis kode yang hanya ditargetkan untuk pengguna di peramban modern, silakan dan gunakan sekarang. Jika tidak, Anda mungkin harus menunggu beberapa saat agar pengguna dapat mengejar ketinggalan.


9
bagus, pendekatan modern. Diuji di Opera, mari kita lupakan IE
Adi Prasetyo

2
Ini adalah pendekatan yang efektif dan sangat bersih; namun, (setidaknya di Chrome 50) ini memecah penanganan tag script. Dengan kata lain, menggunakan metode ini untuk membuat tag skrip dan kemudian menambahkannya ke dokumen (badan atau kepala) tidak menghasilkan tag yang dievaluasi dan karenanya mencegah skrip dieksekusi. (Ini mungkin dengan desain jika evaluasi terjadi pada lampiran; saya tidak bisa mengatakan dengan pasti.)
shanef22

1
MENYENANGKAN! Anda bahkan dapat meminta elemen dengan melakukan sesuatu seperti: template.content.querySelector ("img");
Roger Gajraj

1
@Crescent Fresh: Cukup adil, saya merasa Anda menginginkan jawaban Anda hilang karena Anda mengatakan itu tidak relevan, permintaan maaf saya. Kompromi yang baik akan menambahkan pernyataan pada jawaban Anda yang menunjuk pembaca ke yang ini, seperti yang sekarang telah dibahas pada meta - mari kita berharap bahwa perang edit tidak mengambil kembali dari sana.
BoltClock

2
Ada satu masalah dengan itu. Jika Anda memiliki tag di sana dengan spasi di dalam (!) Di awal atau akhir, mereka akan dihapus! Jangan salah paham, ini bukan tentang spasi yang dihapus oleh html.trimtetapi oleh penguraian dalam pengaturanHTHTML. Dalam kasus saya ini menghapus ruang penting yang menjadi bagian dari textNode. :-(
e-motiv

110

Gunakan insertAdjacentHTML () . Ini bekerja dengan semua browser saat ini, bahkan dengan IE11.

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


9
Pertama, insertAdjacentHTMLbekerja dengan semua browser sejak IE 4.0.
Jonas Äppelgran

6
Kedua, bagus! Nilai tambah yang besar dibandingkan innerHTML += ...adalah bahwa referensi ke elemen sebelumnya masih utuh menggunakan metode ini.
Jonas Äppelgran

7
Ketiga, nilai yang mungkin untuk argumen pertama adalah: beforebegin, afterbegin, beforeend, afterend. Lihat artikel MDN.
Jonas Äppelgran

jawaban terbaik untuk saya
Jordan

4
Sayang sekali itu tidak mengembalikan HTML yang dimasukkan sebagai elemen
Mojimi

30

Implementasi DOM yang lebih baru miliki range.createContextualFragment, yang melakukan apa yang Anda inginkan dengan cara kerangka kerja independen.

Ini didukung secara luas. Namun yang pasti, periksa kompatibilitasnya di tautan MDN yang sama, karena akan berubah. Pada Mei 2017 ini dia:

Feature         Chrome   Edge   Firefox(Gecko)  Internet Explorer   Opera   Safari
Basic support   (Yes)    (Yes)  (Yes)           11                  15.0    9.1.2

3
Perhatikan bahwa ini memiliki kelemahan yang mirip dengan pengaturan innerHTMLdiv; elemen-elemen tertentu, seperti tds, akan diabaikan dan tidak muncul dalam fragmen yang dihasilkan.
Mark Amery

"Ada laporan yang Safari desktop lakukan pada satu titik mendukung Range.createContextualFragment (), tetapi tidak didukung setidaknya di Safari 9.0 dan 9.1." (Tautan MDN dalam jawabannya)
akauppi

27

Tidak perlu mengubah, Anda mendapatkan API asli:

const toNodes = html =>
    new DOMParser().parseFromString(html, 'text/html').body.childNodes[0]

9
Ini menderita kelemahan utama yang sama dengan jawaban yang diterima - itu akan memotong-motong seperti HTML <td>text</td>. Ini karena DOMParsersedang mencoba mem-parsing dokumen HTML lengkap , dan tidak semua elemen valid sebagai elemen root dari dokumen.
Mark Amery

4
-1 karena ini merupakan duplikat dari jawaban sebelumnya yang secara eksplisit menunjukkan kelemahan yang disebutkan dalam komentar saya di atas.
Mark Amery

20

Untuk fragmen html tertentu seperti <td>test</td>, div.innerHTML, DOMParser.parseFromString dan range.createContextualFragment (tanpa konteks yang tepat) solusi yang disebutkan dalam jawaban lain di sini, tidak akan membuat <td>elemen.

jQuery.parseHTML () menanganinya dengan benar (saya mengekstrak fungsi parseHTML jQuery 2 menjadi fungsi independen yang dapat digunakan dalam basis kode non-jquery).

Jika Anda hanya mendukung Edge 13+, lebih mudah menggunakan tag template HTML5:

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

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

16

Inilah cara sederhana untuk melakukannya:

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

4
@MarkAmery perbedaannya di sini adalah bahwa ia menggunakan sebuah fragmen untuk memungkinkan beberapa elemen root ditambahkan di DOM, yang merupakan manfaat tambahan. Kalau saja William menyebutkan itu adalah jawabannya ...
Koen.

10

Anda dapat membuat node DOM yang valid dari string menggunakan:

document.createRange().createContextualFragment()

Contoh berikut menambahkan elemen tombol di halaman yang mengambil markup dari sebuah string:

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


5
Meskipun ini menciptakan DocumentFragmentobjek, tetap saja - sangat mengejutkan saya - menderita cacat yang sama dengan jawaban yang diterima: jika Anda melakukannya document.createRange().createContextualFragment('<td>bla</td>'), Anda mendapatkan sebuah fragmen yang hanya berisi teks 'bla' tanpa <td>elemen. Atau setidaknya, itulah yang saya amati di Chrome 63; Saya belum menyelidiki spesifikasi untuk mencari tahu apakah itu perilaku yang benar atau tidak.
Mark Amery

9

Dengan Prototipe, Anda juga dapat melakukan:

HTML:

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

JS:

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

Apakah ini jQuery? atau JS?
Jeya Suriya Muthumari

1
Ini menggunakan jQuery bukan metode DOM bawaan seperti op yang disebutkan.
Juan Hurtado

1
@JuanHurtado ini menggunakan PrototypeJs seperti yang diminta OP hampir 11 tahun yang lalu;)
Pablo Borowicz

8

Saya menggunakan metode ini (Bekerja di IE9 +), meskipun tidak akan menguraikan <td>atau anak-anak langsung lainnya yang tidak valid:

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

Untuk meningkatkan selanjutnya berguna .toDOM () snippet yang kita dapat menemukan di tempat yang berbeda, kita sekarang dapat dengan aman menggunakan tanda kutip mundur ( literal Template ).

Jadi kita dapat memiliki kutipan tunggal dan ganda dalam deklarasi foo html.

Ini berperilaku seperti heredocs bagi mereka yang akrab dengan istilah tersebut.

Ini dapat ditingkatkan lebih lanjut dengan variabel, untuk membuat templating kompleks:

Literal templat terlampir oleh karakter tick-back ( ) (aksen kubur) alih-alih tanda kutip ganda atau tunggal. Literal templat dapat berisi placeholder. Ini ditunjukkan oleh tanda dolar dan kurung kurawal ($ {ekspresi}). Ekspresi di placeholder dan teks di antara mereka diteruskan ke fungsi. Fungsi default hanya menggabungkan bagian-bagian menjadi satu string. Jika ada ekspresi yang mendahului templat literal (tag di sini), ini disebut "template yang ditandai". Jika demikian, ekspresi tag (biasanya suatu fungsi) dipanggil dengan literal templat yang diproses, yang kemudian dapat Anda manipulasi sebelum menghasilkan. Untuk menghindari tanda centang kembali dalam literal templat, beri tanda garis miring terbalik \ sebelum tanda centang belakang.

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}

Jadi, mengapa tidak menggunakannya secara langsung .innerHTML +=? Dengan melakukannya, seluruh DOM sedang dihitung ulang oleh browser, itu jauh lebih lambat.

https://caniuse.com/template-literals


4

Saya menambahkan Documentprototipe yang membuat elemen dari string:

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

Perhatikan bahwa - sebagaimana ditunjukkan di bagian lain halaman ini - ini tidak akan berfungsi untuk tds.
Mark Amery

3

HTML5 & ES6

<template>

Demo

"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>


2

Terlambat tetapi hanya sebagai catatan;

Dimungkinkan untuk menambahkan elemen sepele ke elemen target sebagai wadah dan menghapusnya setelah digunakan.

// Diuji pada chrome 23.0, firefox 18.0, yaitu 7-8-9 dan 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

Solusi tercepat untuk membuat DOM dari string:

let render = (relEl, tpl, parse = true) => {
  if (!relEl) return;
  const range = document.createRange();
  range.selectNode(relEl);
  const child = range.createContextualFragment(tpl);
  return parse ? relEl.appendChild(child) : {relEl, el};
};

Dan di sini Anda dapat memeriksa kinerja untuk manipulasi DOM React vs JS asli

Sekarang Anda cukup menggunakan:

let element = render(document.body, `
<div style="font-size:120%;line-height:140%">
  <p class="bold">New DOM</p>
</div>
`);

Dan tentu saja dalam waktu dekat Anda menggunakan referensi dari memori karena var "elemen" adalah DOM baru Anda di dokumen Anda.

Dan ingat "innerHTML =" sangat lambat: /


1

Ini kode saya, dan berfungsi:

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

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

1
Gagal jika elemen yang akan dibuat itu sendiri adalah sebuah tabel.
Mark Amery

0

Untuk itu saya pikir saya akan berbagi ini dengan pendekatan yang rumit tapi sederhana saya datang dengan ... Mungkin seseorang akan menemukan sesuatu yang berguna.

/*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

Mengapa tidak lakukan dengan js asli?

    var s="<span class='text-muted' style='font-size:.75em; position:absolute; bottom:3px; left:30px'>From <strong>Dan's Tools</strong></span>"
    var e=document.createElement('div')
    var r=document.createRange();
    r.selectNodeContents(e)
    var f=range.createContextualFragment(s);
    e.appendChild(f);
    e = e.firstElementChild;

-1
function domify (str) {
  var el = document.createElement('div');
  el.innerHTML = str;

  var frag = document.createDocumentFragment();
  return frag.appendChild(el.removeChild(el.firstChild));
}

var str = "<div class='foo'>foo</div>";
domify(str);

-2

Anda dapat menggunakan fungsi berikut untuk mengonversi teks "HTML" ke elemen

function htmlToElement(html)
{
  var element = document.createElement('div');
  element.innerHTML = html;
  return(element);
}
var html="<li>text and html</li>";
var e=htmlToElement(html);


-1; ini adalah teknik yang sama seperti yang diusulkan dalam jawaban yang diterima dan memiliki kelemahan yang sama - terutama, tidak bekerja untuk tds.
Mark Amery

-3
var jtag = $j.li({ child:'text' }); // Represents: <li>text</li>
var htmlContent = $('mylist').html();
$('mylist').html(htmlContent + jtag.html());

Gunakan jnerator


-3

Ini kode yang berfungsi untuk saya

Saya ingin mengonversi string ' Teks ' ke elemen HTML

var diva = UWA.createElement('div');
diva.innerHTML = '<a href="http://wwww.example.com">Text</a>';
var aelement = diva.firstChild;

Apa wwww?
Tintin81

-3

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


Terima kasih atas cuplikan kode ini, yang mungkin memberikan bantuan terbatas dan segera. Sebuah penjelasan yang tepat akan sangat meningkatkan nilai jangka panjang dengan menunjukkan mengapa ini adalah solusi yang baik untuk masalah ini dan akan membuatnya lebih bermanfaat untuk pembaca masa depan dengan lainnya, pertanyaan-pertanyaan serupa. Harap edit jawaban Anda untuk menambahkan beberapa penjelasan, termasuk asumsi yang Anda buat.
Dwhitz

-7

Ini juga akan bekerja:

$('<li>').text('hello').appendTo('#mylist');

Rasanya lebih seperti cara jquery dengan panggilan fungsi dirantai.


26
Apakah itu terasa seperti jQuery karena itu jQuery?
Tim Ferrell

5
Baris terakhir itu sangat lucu!
kumarharsh
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.