将HTML字符串附加到DOM

198

如何将一个 HTML 字符串添加到页面中:

var str = '<p>Just some <span>text</span> here</p>';

如何将内容添加到具有id test<div>中?

(顺便说一下,div.innerHTML += str;是不可接受的。)

11个回答

391

1
@alex 这是相同的概念:解析 HTML 字符串并将其放入 DOM 中。但功能不同 - innerHTML 将字符串放入元素中(替换所有子元素),而 insertAdjacentHTML 将其放置在(1)元素之前,(2)元素之后,(3)第一个子元素之前的元素内,或者(4)最后一个子元素之后的元素内。 - Šime Vidas
5
insertAdjacentHTML比将内容附加到innerHTML中要快,因为insertAdjacentHTML不会对元素的现有子元素进行序列化和解析。 - hsivonen
3
此外,insertAdjacentHTML 保持了更改后的表单元素的值,而向 innerHTML 添加内容会重建该元素并丢失所有表单上下文。 - Brandon Gano
很酷,但我建议使用“beforebegin”而不是“beforeend”。 - Ivan Silkin
1
请勿将其与insertAdjacentElement(..)混淆,后者需要一个实际的Element作为其第二个参数,而不是一个字符串:https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement - Abdull
谢谢,我在尝试删除jQuery的append()方法后已经卡了两个小时了。我以为div.innerHTML += str是等效的,但是结果导致了一些动态组件的错误。感谢@alex的解释。 - undefined

46

性能

AppendChild(E)在Chrome和Safari上比其他解决方案快2倍以上,insertAdjacentHTML(F)在Firefox上最快。在所有浏览器中,innerHTML=(B)(不要与+=(A)混淆)是第二快的解决方案,而且比E和F更加方便。

详情

环境设置(2019.07.10)MacOS High Sierra 10.13.4上的Chrome 75.0.3770(64位),Safari 11.1.0(13604.5.6),Firefox 67.0.0(64位)

enter image description here

  • 在Chrome上,E(每秒140k次操作)最快,B(47k)和F(46k)次之,A(332)最慢
  • 在Firefox上,F(94k)最快,其次是B(80k),D(73k),E(64k),C(21k),最慢的是A(466)
  • 在Safari上,E(207k)最快,然后是B(89k),F(88k),D(83k),C(25k),最慢的是A(509)

你可以在你的机器上重放测试here

function A() {    
  container.innerHTML += '<p>A: Just some <span>text</span> here</p>';
}

function B() {    
  container.innerHTML = '<p>B: Just some <span>text</span> here</p>';
}

function C() {    
  $('#container').append('<p>C: Just some <span>text</span> here</p>');
}

function D() {
  var p = document.createElement("p");
  p.innerHTML = 'D: Just some <span>text</span> here';
  container.appendChild(p);
}

function E() {    
  var p = document.createElement("p");
  var s = document.createElement("span"); 
  s.appendChild( document.createTextNode("text ") );
  p.appendChild( document.createTextNode("E: Just some ") );
  p.appendChild( s );
  p.appendChild( document.createTextNode(" here") );
  container.appendChild(p);
}

function F() {    
  container.insertAdjacentHTML('beforeend', '<p>F: Just some <span>text</span> here</p>');
}

A();
B();
C();
D();
E();
F();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

This snippet only for show code used in test (in jsperf.com) - it not perform test itself. 
<div id="container"></div>


可读性通常比性能更重要。但感谢您的辛勤工作。 - Jay
@Jay 优化应该只在需要时使用,并且通常必须进行详细注释。 - Kamil Kiełczewski
使用innerHTML似乎不太好,因为浏览器需要重新解析整个HTML而不是添加到尾部。 - Dee

33

这样可以接受吗?

var child = document.createElement('div');
child.innerHTML = str;
child = child.firstChild;
document.getElementById('test').appendChild(child);

jsFiddle

但是Neil的答案是更好的解决方案。


1
@Šime 使用 DOM API 始终感觉像是在黑客中 :P 你更喜欢在没有 innerHTML 的情况下反序列化字符串吗? - alex
1
@Šime - 我认为只有两种方法可以利用浏览器的HTML解析器,即innerHTML和document.write。如果您认为innerHTML是一种hack... - Alohci
@Šime - 我明白了。我必须承认我不知道这个。我已经为Neil的答案投了赞成票。我正在尝试找到它的规范。developer.mozilla.org的链接不再指向它。 - Alohci
@Alohci,它在HTML5标准下的"3.5动态标记插入"中。有趣的是,由于某种原因,它已经被放弃了HTML Living Standard(这是一个更相关的HTML文档)。 - Šime Vidas
1
@Šime - 谢谢。我找到了WHATWG版本。它在DOM解析和序列化规范中。它指出除了innerHTML和insertAdjacentHTML之外,还有outerHTML和(我想是)createContextualFragment。 - Alohci
显示剩余5条评论

11

这个想法是在一个中间元素上使用 innerHTML,然后通过 appendChild 将其所有子节点移动到实际想要它们的位置。

var target = document.getElementById('test');
var str = '<p>Just some <span>text</span> here</p>';

var temp = document.createElement('div');
temp.innerHTML = str;
while (temp.firstChild) {
  target.appendChild(temp.firstChild);
}

这样做可以避免清除div #test 上的任何事件处理程序,同时仍然允许您附加一串HTML字符串。


4
正确的方法是使用insertAdjacentHTML。如果你的str不包含script标签,那么在Firefox 8之前,你可以回退到使用Range.createContextualFragment
如果你的str包含script标签,你需要在插入片段之前从createContextualFragment返回的片段中删除script元素。否则,这些脚本将会被执行。(insertAdjacentHTML标记脚本为不可执行。)

感谢您提到 createContextualFragment。我正在寻找一种方法,使一些包含脚本的HTML仅在用户同意设置Cookie时运行。 - schliflo

2

快速 黑客攻击


<script>
document.children[0].innerHTML="<h1>QUICK_HACK</h1>";
</script>

使用场景:

1: 保存为.html文件并在chrome、firefox或edge中运行。(IE不可用)

2: 在http://js.do中使用。

实际应用: http://js.do/HeavyMetalCookies/quick_hack

注释说明:

<script>

//: The message "QUICK_HACK" 
//: wrapped in a header #1 tag.
var text = "<h1>QUICK_HACK</h1>";

//: It's a quick hack, so I don't
//: care where it goes on the document,
//: just as long as I can see it.
//: Since I am doing this quick hack in
//: an empty file or scratchpad, 
//: it should be visible.
var child = document.children[0];

//: Set the html content of your child
//: to the message you want to see on screen.
child.innerHTML = text;

</script>

我发布的原因:

JS.do有两个必备功能:

  1. 无自动完成
  2. 垂直监视器友好

但不会显示console.log消息。 我来这里寻找一个快速解决方案。 我只想看到几行scratchpad代码的结果, 其他解决方案太麻烦了。


1

InnerHTML会清除所有现有节点的数据和事件

通过appendChild和firstChild添加到innerHTML中只会添加第一个子元素。例如,如果我们要添加:

 <p>text1</p><p>text2</p>

只有文本1会显示

这个怎么样:

通过附加子元素并然后删除我们创建的标记来编辑outerHTML,添加特殊标记到innerHTML。不知道它有多聪明,但对我来说很有效 或者你可以将outerHTML更改为innerHTML,这样就不必使用replace函数了

function append(element, str)
{

  var child = document.createElement('someshittyuniquetag');
  
  child.innerHTML = str;

  element.appendChild(child);

  child.outerHTML = child.outerHTML.replace(/<\/?someshittyuniquetag>/, '');

// or Even child.outerHTML = child.innerHTML


  
}
<div id="testit">
This text is inside the div
<button onclick="append(document.getElementById('testit'), '<button>dadasasdas</button>')">To div</button>
<button onclick="append(this, 'some text')">to this</button>
</div>


1

这可以解决

 document.getElementById("list-input-email").insertAdjacentHTML('beforeend', '<div class=""><input type="text" name="" value="" class="" /></div>');

3
欢迎来到Stack Overflow。请在回答中解释您的代码,以提供一些上下文而不是仅粘贴一长串代码。尽管您自己理解代码,但其他人可能不理解它正在做什么。请确保您的解释简洁易懂,不要改变原意。 - TResponse

0
为什么那样不可接受?
使用以下代码是标准的做法:
document.getElementById('test').innerHTML += str

9
但这会导致绑定在#test上的事件处理程序失效,通常这是不可取的。 - alex
有趣的是它这样做。实际上似乎并不合乎逻辑。 - Nick Brunt
3
如果你考虑到将HTML序列化为字符串,然后再设置回HTML,这就是合乎逻辑的。 - alex
我猜JavaScript必须再次运行以重置事件处理程序。很公平。 - Nick Brunt
1
@Nick 你不想将DOM的一部分字符串化(序列化),只是为了将其与另一个字符串连接,然后再将整个字符串解析回DOM。正如Alex所说,序列化不会记录绑定的事件处理程序(以及其他内容)。即使序列化可以捕获所有内容,你仍然不想这样做。 - Šime Vidas

-1
我的问题的答案就是一个简单的带有空值的Map;但我却只得到了一个非常笨重而不精确的回答。我想知道是谁关闭了我的问题……甚至都不愿意读一下。
"use strict"
try{
  const fruits = new Map([["Banana",""],["Apples", ""], ["Orange"," "]]);

let text = "<h1>Fruits</h1>" + "<ul>";

fruits.forEach(function(value, key){
  text += "<li>" + key + value + "<br>" + "</li>" });

text +="</li> ";

document.getElementById('demo').innerHTML = text;
}
catch(err){
 document.getElementById('theError').innerHTML = err;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接