Yerleşik DOM yöntemlerini veya Prototip'i kullanarak bir HTML dizesinden yeni bir DOM öğesi oluşturma


621

Ben bir öğe temsil eden bir HTML dizesi vardır: '<li>text</li>'. Bunu DOM'daki bir öğeye eklemek istiyorum ( ulbenim durumumda). Bunu Prototip veya DOM yöntemleriyle nasıl yapabilirim?

(Bunu jQuery'de kolayca yapabileceğimi biliyorum, ama ne yazık ki jQuery kullanmıyoruz.)


Üzgünüm, tam olarak ne yapmaya çalışıyorsun? HTML dizesi -> dom öğesi?
Crescent Fresh

36
Bu çözümlerin dolaylı olması talihsiz bir durumdur. Standartlar komitesinin benzer bir şey belirtmesini isterdim:var nodes = document.fromString("<b>Hello</b> <br>");
Sridhar Sarnobat


1
Yukarıdakilerle ilgili bir sorunum vardı, çünkü geçilmesi gereken niteliklerim vardı ve bunları ayrıştırmak istemiyordum: "<table id = '5ccf9305-4b10-aec6-3c55-a6d218bfb107' class = 'data-table satır sınırı görüntüleme 'hücre aralığı =' 0 'genişlik ='% 100 '> </table> "yani, basitçe kullandım: $ (" <table id =' 5ccf9305-4b10-aec6-3c55-a6d218bfb107 'class =' ​​data -çizgi satır sınırı görüntüleme 'hücre aralığı =' 0 'genişlik ='% 100 '> </table> ")
stevenlacerda 14:07

Yanıtlar:


818

Not: mevcut tarayıcıların çoğu <template>, dizelerden öğe oluşturmayı daha güvenilir bir şekilde sağlayan HTML öğelerini destekler . Ayrıntılar için Mark Amery'nin cevabına bakınız .

Daha eski tarayıcılar ve düğüm / jsdom için : ( <template>yazma sırasında henüz öğeleri desteklemez ), aşağıdaki yöntemi kullanın. Kütüphanelerin bir HTML dizesinden DOM öğelerini almak için kullandığı şeyle aynıdır ( IE'nin uygulanmasıyla hatalar üzerinde çalışabilmesi için bazı ekstra çalışmalarlainnerHTML ):

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

HTML şablonları farklı olarak bu o Not olmaz yasal olarak çocukları olamaz bazı elementlerin için çalışmak <div>gibi, <td>s.

Zaten bir kitaplık kullanıyorsanız, HTML dizelerinden öğe oluşturmak için kitaplık onaylı yönteme bağlı kalmanızı öneririm:


1
Bu güvenli olmayan innerHTML atamasını kullanmadan innerQML'yi jQuery kullanarak oluşturulan div'e ayarlama (div.innerHTML = "bazı değer")
Shivanshu Goyal

12
İşlev adı createElementFromHTML yanıltıcıdır, çünkü örneğin olmayan bir a div.firstChilddöndürür . Bunun yerine işlevden bir dönüş oluşturmak için . NodeHTMLElementnode.setAttributeElementdiv.firstElementChild
Semmel

Teşekkürler, <div>eklediğim HTML'yi sarmak .innerHTMLbeni rahatsız ediyordu. Hiç kullanmayı düşünmedim .firstChild.
Ivan

Oluşturulan içinde bir SVG ayrıştırmaya çalışıyorum ve konsol günlüğü bana doğru DOM öğesi verirken divçıktıdır [object SVGSVGElement]. Neyi yanlış yapıyorum?
ed1nh0

1
FirstChild yerine firstElementChild kullanırım (bkz. W3schools.com/jsref/prop_element_firstelementchild.asp ), çünkü şablonun önünde veya sonunda boşluk varsa, firstChild boş textNode
Chris Panayotoff

342

HTML 5 <template>, bu amaçla kullanılabilecek öğeyi tanıttı (şimdi WhatWG spesifikasyonunda ve MDN belgelerinde açıklandığı gibi ).

Bir <template>öğe, komut dosyalarında kullanılabilecek HTML parçalarını bildirmek için kullanılır. Öğe, DOM'da şablonun içeriğine erişim sağlamak için bir tür özelliğe HTMLTemplateElementsahip olarak temsil edilir . Bu , bir öğeyi ayarlayıp daha sonra ' özelliğine erişerek bir HTML dizesini DOM öğelerine dönüştürebileceğiniz anlamına gelir ..contentDocumentFragmentinnerHTML<template>template.content

Örnekler:

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

Farklı bir kap öğesi kullanandiv benzer yaklaşımların işe yaramadığını unutmayın. HTML'de, diğer öğe türlerinin içinde hangi öğe türlerinin bulunmasına izin verildiği konusunda kısıtlamalar vardır; örneğin, tda öğesini doğrudan a div. Bu ayarlamaya çalışırsanız kaybolmak bu öğeleri neden innerHTMLa divbunları içerecek şekilde. Yana <template>ler, içeriklerinde böyle kısıtlamaları vardır bir şablon kullanırken, bu eksiklik geçerli değildir.

Ancak, templatebazı eski tarayıcılarda desteklenmez. Ocak 2018 itibarıyla, kullanabilir miyim ... Dünya genelinde kullanıcıların% 90'ının templates destekleyen bir tarayıcı kullandığını tahmin ediyor . Özellikle, hiçbir Internet Explorer sürümü bunları desteklemez; Microsoft template, Edge piyasaya sürülene kadar destek uygulamadı .

Yalnızca modern tarayıcılardaki kullanıcıları hedefleyen kod yazabileceğiniz kadar şanslıysanız, devam edin ve bunları hemen kullanın. Aksi takdirde, kullanıcıların yetişmesi için bir süre beklemeniz gerekebilir.


9
güzel, modern bir yaklaşım. Opera'da test edildi, IE'yi unutalım
Adi Prasetyo

2
Bu etkili bir yaklaşım ve çok temiz; ancak, (en azından Chrome 50'de) bu komut dosyası etiketinin işlenmesini bozar. Başka bir deyişle, bir komut dosyası etiketi oluşturmak için bu yöntemi kullanmak ve ardından belgeye (gövde veya kafa) eklemek etiketin değerlendirilmesine neden olmaz ve dolayısıyla komut dosyasının yürütülmesini engeller. (Bu ek üzerinde değerlendirme olursa tasarım gereği olabilir; kesin olarak söyleyemem.)
shanef22

1
GÜZEL! hatta böyle bir şey yaparak öğeleri sorgulayabilirsiniz: template.content.querySelector ("img");
Roger Gajraj

1
@Crescent Fresh: Yeterince adil, şimdi ilgisiz olduğunu söylediğin için cevabının gitmesini istediğin izlenimindeydim, özür dilerim. İyi bir uzlaşma, şu anda meta üzerinde tartışıldığı gibi , okuyucularınıza bu cevabı işaret eden bir ifade eklemek olacaktır - hadi, düzenleme savaşının oradan geri dönmeyeceğini umalım.
BoltClock

2
Bununla ilgili bir sorun var. Orada, başında veya sonunda boşluklar (!) Olan bir etiketiniz varsa, bunlar kaldırılır! Beni yanlış anlamayın, bu kaldırılan alanlarla html.trimdeğil, innerHTML ayarının içsel ayrıştırmasıyla ilgilidir. Benim durumumda bir textNode parçası olan önemli boşlukları kaldırır. :-(
e-motiv

109

İnsertAdjacentHTML () kullanın . IE11 ile bile mevcut tüm tarayıcılarla çalışır.

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


9
İlk olarak, insertAdjacentHTMLIE 4.0'dan beri tüm tarayıcılarla çalışır.
Jonas Äppelgran

6
İkincisi, harika! Buna kıyasla büyük bir artı innerHTML += ..., önceki elemanlara yapılan referansların bu yöntem kullanılarak hala bozulmamış olmasıdır.
Jonas Äppelgran

7
İlk argüman için Üçüncüsü, olası değerleri geçerli: beforebegin, afterbegin, beforeend, afterend. MDN makalesine bakın.
Jonas Äppelgran

bana en iyi cevap
Ürdün

4
Eklenen HTML'yi öğe olarak
döndürmüyor

30

range.createContextualFragmentİstediğinizi çerçeveden bağımsız bir şekilde yapan daha yeni DOM uygulamaları vardır .

Yaygın olarak desteklenir. Ancak emin olmak için uyumluluğunu değiştireceği gibi aynı MDN bağlantısında kontrol edin . Mayıs 2017 itibariyle bu:

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

3
Bunun innerHTMLdiv değerinin ayarlanmasına benzer dezavantajları olduğunu unutmayın ; tds gibi bazı elemanlar yok sayılır ve ortaya çıkan parçada görünmez.
Mark Amery

"Masaüstü Safari'nin Range.createContextualFragment () işlevini bir noktada desteklediği raporları var, ancak en azından Safari 9.0 ve 9.1'de desteklenmiyor." (
Cevabında

27

Herhangi bir değişiklik yapmanıza gerek yok, yerel bir API'nız var:

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

9
Bu, kabul edilen cevapla aynı büyük dezavantajdan muzdariptir - HTML'yi değiştirecektir <td>text</td>. Bunun nedeni DOMParser, tam bir HTML belgesini ayrıştırmaya çalışmaktır ve tüm öğeler bir belgenin kök öğeleri olarak geçerli değildir.
Mark Amery

4
-1 çünkü bu, yukarıdaki yorumumda belirtilen dezavantajı açıkça işaret eden daha önceki bir cevabın kopyası .
Mark Amery

20

<td>test</td>Buradaki diğer yanıtlarda belirtilen div.innerHTML, DOMParser.parseFromString ve range.createContextualFragment (doğru içerik olmadan) gibi bazı html parçaları için <td>öğe oluşturulmaz.

jQuery.parseHTML () bunları düzgün bir şekilde işler ( jQuery 2'nin parseHTML işlevini jquery olmayan kod tabanlarında kullanılabilen bağımsız bir işleve çıkardım ).

Yalnızca Edge 13+ sürümünü destekliyorsanız, HTML5 şablon etiketini kullanmak daha kolaydır:

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

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

16

Heres bunu yapmanın basit bir yolu:

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
Buradaki fark, DOM'a birden fazla kök öğenin eklenmesine izin vermek için bir parça kullanmasıdır, bu da ek bir avantajdır. William bunun cevabını söylemiş olsaydı ...
Koen.

10

Aşağıdakileri kullanarak bir dizeden geçerli DOM düğümleri oluşturabilirsiniz:

document.createRange().createContextualFragment()

Aşağıdaki örnek, sayfaya bir dizeden biçimlendirmeyi alan bir düğme öğesi ekler:

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
Bu bir DocumentFragmentnesne oluştursa bile , yine de - büyük sürprizime göre - kabul edilen cevapla aynı kusurdan muzdariptir: yaparsanız document.createRange().createContextualFragment('<td>bla</td>'), <td>öğe olmadan sadece 'bla' metnini içeren bir parça alırsınız . Ya da en azından Chrome 63'te gözlemlediğim şey bu; Doğru davranış olup olmadığını anlamak için spesifikasyona girmedim .
Mark Amery

9

Prototip ile şunları da yapabilirsiniz:

HTML:

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

JS:

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

Bu jQuery mi? veya JS?
Jeya Suriya Muthumari

1
Bu op belirtildiği gibi yerleşik değil DOM yöntemleri jQuery kullanıyor.
Juan Hurtado

1
@JuanHurtado Bu OP yaklaşık 11 yıl önce talep edildiği gibi Prototipler kullanıyor;)
Pablo Borowicz

8

Bu yöntemi (IE9 + 'da çalışır), ancak ayrıştırmayacak <td>veya vücudun diğer geçersiz doğrudan alt öğelerini kullanmıyorum :

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

Ayrıca kullanışlı arttırmak için () .toDOM biz farklı yerlerde bulabileceğini pasajı, şimdi olabilir güvenle kullanabilir komutu ters tırnak ( şablon değişmezleri ).

Bu yüzden foo html bildiriminde tek ve çift tırnaklara sahip olabiliriz .

Bu terime aşina olanlar için heredoc gibi davranır .

Bu karmaşık değişkenler yapmak için değişkenlerle daha da geliştirilebilir:

Şablon değişmezleri, çift ​​veya tek tırnak yerine arka-işaret ( ) (mezar aksanı) karakteri ile çevrelenir . Şablon değişmezleri yer tutucu içerebilir. Bunlar dolar işareti ve süslü parantez ($ {expression}) ile gösterilir. Yer tutuculardaki ifadeler ve aralarındaki metin bir işleve aktarılır. Varsayılan işlev, parçaları tek bir dizede birleştirir. Şablon değişmezinden (burada etiket) önce bir ifade varsa, buna "etiketli şablon" denir. Bu durumda, etiket ifadesi (genellikle bir işlev) işlenen şablon değişmeziyle çağrılır; bu işlem daha sonra çıktı almadan önce değiştirebilirsiniz. Şablon değişmezinde bir geri işaretten kaçmak için, arka işaretin önüne bir ters eğik çizgi \ koyun.

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}

Peki, neden doğrudan kullanmıyorsunuz .innerHTML +=? Bunu yaparak, tüm DOM tarayıcı tarafından yeniden hesaplanıyor, çok daha yavaş.

https://caniuse.com/template-literals


4

DocumentDizeden bir öğe oluşturan bir prototip ekledim :

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

Bu sayfanın başka bir yerinde belirtildiği gibi , bununtd s için işe yaramayacağını unutmayın .
Mark Amery

3

HTML5 ve ES6

<template>

gösteri

"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

Geç ama sadece bir not olarak;

Hedef öğeye kap olarak önemsiz bir öğe eklemek ve kullandıktan sonra kaldırmak mümkündür.

// 23.0 kromunda test edildi, firefox 18.0, yani 7-8-9 ve 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

DOM'u dizeden oluşturmak için en hızlı çözüm:

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

Ve burada yerel manipülasyon React vs yerli JS performansını kontrol edebilirsiniz

Şimdi u kullanabilirsiniz:

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

Ve tabii ki yakın gelecekte u bellek neden referanslar var var "element" yeni oluşturulan DOM belgenizde.

Ve unutmayın "innerHTML =" çok yavaş: /


1

İşte benim kod, ve çalışıyor:

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

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

1
Oluşturulacak öğenin kendisi bir tablo ise başarısız olur.
Mark Amery

0

Bunun için, bunu ortaya çıkardığım karmaşık ama yine de basit bir yaklaşımla paylaşacağımı sanıyordum ... Belki birisi faydalı bir şeyler bulabilir.

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

Neden yerli js ile yapmıyorsunuz?

    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

"HTML" metnini öğeye dönüştürmek için aşağıdaki işlevi kullanabilirsiniz

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; bu, kabul edilen cevapta önerilen teknikle aynıdır ve dezavantajları vardır - özellikle de tds için çalışmıyor .
Mark Amery

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

Jnerator kullanın


-3

İşte benim için çalışma kodu

' Metin ' dizesini HTML öğesine dönüştürmek istedim

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

Nedir wwww?
Tenten81

-3

msg = "test" jQuery.parseHTML (msg)


Sınırlı ve anında yardım sağlayabilecek bu kod snippet'i için teşekkür ederiz. Bir Doğru bir açıklama ölçüde bu soruna iyi bir çözüm olmasının nedeni göstererek uzun vadeli değerini artıracak ve diğer benzer sorularla gelecek okuyucularına daha kullanışlı bir hale getirecektir. Yaptığınız varsayımlar dahil bazı açıklamalar eklemek için lütfen yanıtınızı düzenleyin .
Dwhitz

-7

Bu da işe yarayacak:

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

Zincirleme fonksiyon çağrıları ile daha çok jquery gibi hissediyor.


26
JQuery olduğu için jQuery gibi mi hissediyor?
Tim Ferrell

5
Bu son çizgi çok komik!
kumarharsh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.