比方说,我们想在初始加载后给网页添加一些东西。JavaScript给了我们各种各样的工具。也许你已经使用了其中的一些,比如append,appendChild,insertAdjacentHTML, 或者innerHTML 。
用JavaScript追加和插入东西的困难之处,不在于它所提供的工具,而在于使用哪一种,何时使用,以及理解每一种工具是如何工作的。
让我们试着把事情弄清楚。
超级快速的背景
在开始之前,讨论一下背景可能会有帮助。在最简单的层面上,一个网站是一个从服务器上下载到浏览器的HTML文件。

浏览器将HTML文件中的HTML标签转换为一堆对象,可以用JavaScript进行操作。这些对象构建了一个文档对象模型(DOM)树。这棵树是一系列的对象,其结构是父子关系。
在DOM术语中,这些对象被称为节点,或者更具体地说,HTML元素。
<!-- I'm the parent element -->
<div>
<!-- I'm a child element -->
<span>Hello</span>
</div>
在这个例子中,HTMLspan 元素是div 元素的孩子,而 元素是父对象。
我知道,这些术语中有些很奇怪,可能会让人感到困惑。我们说 "节点",但其他时候我们可能会说 "元素 "或 "对象 "来代替。而且,在某些情况下,它们指的是同一件事,只是取决于我们想做得多具体。
例如,"元素 "是 "节点 "的一种特定类型,就像苹果是一种特定类型的水果。
我们可以把这些术语从最一般的到最具体的组织起来。对象→节点→元素→HTML元素
了解这些DOM项目是很重要的,因为我们将与它们互动,在最初的页面加载后用JavaScript添加和附加一些东西。事实上,让我们开始工作吧。
设置
这些追加和插入方法大多遵循这种模式。
Element.append_method_choice(stuff_to_append)
同样,一个元素只是DOM树中的一个对象,它代表一些HTML。早些时候,我们曾提到,DOM树的目的是给我们提供一种方便的方式,用JavaScript与HTML交互。
那么,我们如何使用JavaScript来抓取一个HTML元素呢?
查询DOM
假设我们有下面这一小段的HTML。
<div id="example" class="group">
Hello World
</div>
有几种常见的方法来查询DOM。
// Query a specific selector (could be class, ID, element type, or attribute):
const my_element1 = document.querySelector('#example')
// Query an element by its ID:
const my_element2 = document.getElementbyId('example')
// Query an element by its class:
const my_element3 = document.getElementsbyClassName('group')[0]
在这个例子中,所有的三行都查询同样的东西,但以不同的方式寻找它。一个是看项目的任何一个CSS选择器;一个是看项目的ID;还有一个是看项目的类。
请注意,getElementbyClass 方法返回一个数组。这是因为它能够匹配DOM中的多个元素,并将这些匹配的元素存储在一个数组中,以确保所有的元素都被计算在内。
我们可以追加和插入什么
// Append Something
const my_element1 = document.querySelector('#example')
my_element1.append(something)
在这个例子中,something 是一个参数,代表了我们想要添加到匹配元素末尾的东西(即追加到)。
我们不能只是把任何旧的东西附加到任何旧的对象上。append 方法只允许我们将节点或纯文本追加到DOM中的一个元素。但是其他一些方法也可以将HTML追加到DOM元素上。
- 节点要么是用JavaScript中的
document.createElement(),要么是用我们在上一节中看到的查询方法之一来选择。 - 纯文本是,嗯,文本。它是纯文本,因为它不带有任何HTML标签或格式化。(例如:
Hello)。 - HTML也是文本,但与纯文本不同,当它被添加到DOM中时,确实会被解析为标记(例如:
<div>Hello</div>)。
绘制出哪些方法支持哪些参数可能会有帮助。
| 方法 | 节点 | HTML文本 | 文本 |
|---|---|---|---|
append | 有 | 没有 | 是的 |
appendChild | 有 | 不 | 不 |
insertAdjacentHTML | 不 | 是 | 是1 |
innerHTML2 | 不 | 是 | 是 |
1这个方法可行,但 [insertAdjacentText](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentText)推荐使用。
2不采取传统的参数,而是采用innerHTML ,如。element.innerHTML = 'HTML String'
如何选择使用哪种方法
嗯,这真的取决于你想要追加的内容,更不用说要解决某些浏览器的怪癖了。
- 如果你有现有的HTML被发送到你的JavaScript,使用支持HTML的方法可能是最容易的。
- 如果你要在JavasScript中建立一些新的HTML,创建一个带有大量标记的节点可能会很麻烦,而HTML则不那么冗长。
- 如果你想立即附加事件监听器,你会想用节点来工作,因为我们在节点上调用
[addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)节点,而不是HTML。 - 如果你需要的只是文本,任何支持纯文本参数的方法都是可以的。
- 如果你的HTML是潜在的不可信的(即它来自于用户的输入,比如说博客文章的评论),那么你在使用HTML时就要小心了,除非它已经被净化了(即有害代码已经被删除)。
- 如果你需要支持Internet Explorer,那么使用
append是不可能的。
例子
假设我们有一个聊天应用程序,我们想在用户戴尔登录时将其追加到好友列表中。
<!-- HTML Buddy List -->
<ul id="buddies">
<li><a>Alex</a></li>
<li><a>Barry</a></li>
<li><a>Clive</a></li>
<!-- Append next user here -->
</ul>
下面是我们如何使用上面的每个方法来完成这个任务。
[append](https://developer.mozilla.org/en-US/docs/Web/API/Element/append)
我们需要创建一个节点对象,将其转换为<li><a>Dale</a></li> 。
const new_buddy = document.createElement('li')
const new_link = document.createElement('a')
const buddy_name = "Dale"
new_link.append(buddy_name) // Text param
new_buddy.append(new_link) // Node param
const list = document.querySelector('#buddies')
list.append(new_buddy) // Node param
我们最后的append ,将新用户放在好友列表的最后,就在结束的</ul> 标签之前。如果我们想把用户放在列表的前面,我们可以使用 [prepend](https://developer.mozilla.org/en-US/docs/Web/API/Element/prepend)方法来代替。
你可能已经注意到,我们也能够使用append ,在我们的<a> 标签中填充这样的文本。
const buddy_name = "Dale"
new_link.append(buddy_name) // Text param
这突出了append 的多功能性。
再一次指出,append 在Internet Explorer中是不支持的。
[appendChild](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild)
appendChild 是我们的另一个JavaScript方法,用于向DOM元素添加东西。它有一点局限性,因为它只适用于节点对象,所以我们需要一些帮助,从 [textContent](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)(或 [innerText](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/innerText))来满足我们的纯文本需求。
请注意,appendChild ,不同于append ,在Internet Explorer中被支持。
const new_buddy = document.createElement('li')
const new_link = document.createElement('a')
const buddy_name = "Dale"
new_link.textContent = buddy_name
new_buddy.appendChild(new_link) // Node param
const list = document.querySelector('#buddies')
list.appendChild(new_buddy) // Node param
在继续之前,让我们考虑一个类似的例子,但要有更多的标记。
假设我们想附加的HTML不是像<li><a>Dale</a></li> ,而是像。
<li class="abc" data-tooltip="Click for Dale">
<a id="user_123" class="def" data-user="dale">
<img src="images/dale.jpg" alt="Profile Picture"/>
<span>Dale</span>
</a>
</li>
我们的JavaScript会看起来像这样。
const buddy_name = "Dale"
const new_buddy = document.createElement('li')
new_buddy.className = 'abc'
new_buddy.setAttribute('data-tooltip', `Click for ${buddy_name}`)
const new_link = document.createElement('a')
new_link.id = 'user_123'
new_link.className = 'def'
new_link.setAttribute('data-user', buddy_name)
const new_profile_img = document.createElement('img')
new_profile_img.src = 'images/dale.jpg'
new_profile_img.alt = 'Profile Picture'
const new_buddy_span = document.createElement('span')
new_buddy_span.textContent = buddy_name
new_link.appendChild(new_profile_img) // Node param
new_link.appendChild(new_buddy_span) // Node param
new_buddy.appendChild(new_link) // Node param
const list = document.querySelector('#buddies')
list.appendChild(new_buddy) // Node param
没有必要遵循以上所有的JavaScript--重点是,用JavaScript创建大量的HTML会变得相当麻烦。而如果我们使用append 或appendChild ,这一点是无法绕过的。
在这种重度标记的情况下,把我们的HTML写成一个字符串,而不是使用一堆JavaScript方法,可能会很好...
[insertAdjacentHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML)
insertAdjacentHTML 是像 ,因为它也能够向DOM元素添加东西。但有一点不同的是, ,在相对于匹配元素的特定位置插入这些东西。append insertAdjacentHTML
而且它恰好能与HTML一起工作。这意味着我们可以在DOM元素中插入实际的HTML,并准确地指出我们想要的四个不同位置。
<!-- beforebegin -->
<div id="example" class="group">
<!-- afterbegin -->
Hello World
<!-- beforeend -->
</div>
<!-- afterend -->
因此,我们可以通过在#buddies 选择器的beforeend 位置上插入HTML,有点像复制 "附加 "我们的HTML的想法。
const buddy_name = "Dale"
const new_buddy = `<li><a>${buddy_name}</a></li>`
const list = document.querySelector('#buddies')
list.insertAdjacentHTML('beforeend', new_buddy)
记住我们前面提到的安全问题。我们永远不想插入由最终用户提交的HTML,因为我们会让自己暴露在跨站脚本漏洞之下。
[innerHTML](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML)
innerHTML 是另一种插入东西的方法。这就是说,不建议用它来插入,我们会看到。
这里是我们的查询和我们想要插入的HTML。
const buddy_name = "Dale"
const new_buddy = `<li><a>${buddy_name}</a></li>`
const list = document.querySelector('#buddies')
list.innerHTML += new_buddy
最初,这似乎是有效的。我们更新的好友列表在DOM中看起来像这样。
<ul id="buddies">
<li><a>Alex</a></li>
<li><a>Barry</a></li>
<li><a>Clive</a></li>
<li><a>Dale</a></li>
</ul>
这就是我们想要的!但是,使用innerHTML 有一个限制,它使我们不能在#buddies 内的任何元素上使用事件监听器,因为+= 在list.innerHTML += new_buddy 的性质。
你看,A += B 的行为与A = A + B 相同。在这种情况下,A 是我们现有的HTML,而B 是我们要插入到它的内容。问题是,这样做的结果是在现有的HTML中复制了额外插入的HTML。而事件监听者无法监听到副本。这意味着如果我们想监听好友列表中任何一个<a> 标签的点击事件,我们就会因为innerHTML 而失去这个能力。
所以,只是要注意这一点。
演示
这里有一个演示,它将我们所涉及的所有方法集中起来。点击每个方法的按钮,在好友列表中插入 "Dale "作为一个项目。
CodePen嵌入回退
继续并打开DevTools,看看新的列表项是如何被添加到DOM中的。
回顾一下
这是一个关于我们在DOM中追加和插入东西时的总体概述。当你需要帮助来确定使用哪种方法时,就把它当作一个小抄吧。
| 方法 | 节点
| HTML文本 | 文本
| Internet Explorer? | 事件监听器 | 安全吗?
| HTML模板化 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| append | 有 | 没有 | 是的 | 不 | 保留 | 是 | 中等 |
| appendChild | 是 | 不 | 不 | 是 | 保留 | 是 | 中等 |
| insertAdjacentHTML | 不 | 有 | 是1 | 是 | 保留 | 谨慎的 | 简单 |
| innerHTML2 | 没有 | 有 | 有 | 是的 | 损失 | 小心翼翼 | 简单 |
1这可以,但建议使用insertAdjacentText 。
2不采取传统的参数,而是采用innerHTML ,如。element.innerHTML = 'HTML String'
如果我必须把所有这些浓缩成几个建议。
- 不建议使用
innerHTML进行追加,因为它删除了事件监听器。 append如果你喜欢使用节点元素或纯文本的灵活性,并且不需要支持Internet Explorer,那么这个方法很好。appendChild如果你喜欢(或需要)使用节点元素,并希望得到全浏览器的支持,那么它的效果很好。insertAdjacentHTML如果你需要生成HTML,并希望更具体地控制它在DOM中的位置,那就很好。