对用JavaScript进行追加和插入的方法的比较

314 阅读8分钟

比方说,我们想在初始加载后给网页添加一些东西。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元素上。

  1. 节点要么是用JavaScript中的document.createElement() ,要么是用我们在上一节中看到的查询方法之一来选择。
  2. 纯文本是,嗯,文本。它是纯文本,因为它不带有任何HTML标签或格式化。(例如:Hello)。
  3. 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会变得相当麻烦。而如果我们使用appendappendChild ,这一点是无法绕过的。

在这种重度标记的情况下,把我们的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中的位置,那就很好。