文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

当jQuery发布时,其流行的主要原因之一就是可以轻松地选择DOM元素,遍历并修改其内容。 但那是2006年的事了。在那些日子里,我们被Internet Explorer 7所困扰,而离ECMAScript 5还有几年的时间。

来自作者的更多内容

幸运的是,从那以后,很多事情都发生了变化。 浏览器已经变得更加符合标准,原生JavaScript也有了跨越式发展。 随着情况的改善,我们看到人们正在质疑是否仍然需要jQuery。 我不打算在这里讨论这个问题,而是想提供一些想法,因为我提供了六个原生DOM操作方法,这些方法受到了这个伟大的库的启发。 这些方法如下:

第 1 段(可获 1.96 积分)

在本文中,我想研究一下这些原生DOM操作方法与其jQuery对应方法之间的相似之处和不同之处。 那么希望下次你发现自己为了方便包含jQuery库时,你可能会选择拥抱原生JavaScript的力量。

1. append()

append 方法执行插入操作,即将节点添加到DOM树。 如名称所示,它将传递的参数附加 到调用它的节点的子列表中。 请考虑以下示例:

第 2 段(可获 1.28 积分)
const parent = document.createElement('div')
const child1 = document.createElement('h1')
parent.append(child1)
parent.outerHTML
// <div><h1></h1></div>

const child2 = document.createElement('h2')
parent.append(child2)
parent.outerHTML
// <div><h1></h1><h2></h2></div>

如果您询问它与原生appendChild方法的不同之处,这是情有可原的。 嗯,第一个区别是append()可以一次使用多个参数,相应的节点将被附加到子列表中,就像 jQuery append方法一样。 继续前面的代码段:

第 3 段(可获 0.64 积分)
const child3 = document.createElement('p')
const child4 = document.createElement('p')
const child5 = document.createElement('p')
parent.append(child3, child4, child5)
parent.outerHTML
/* Outputs:
<div>
  <h1></h1>
  <h2></h2>
  <p></p>
  <p></p>
  <p></p>
</div>
*/

而且参数甚至可以是一个字符串。所以,使用 appendChild() 语法则会比较冗长:

parent.appendChild(document.createTextNode('just some text'))

而 append() 则会比较简短:

parent.append('just some text')
parent.outerHTML
/* Outputs:
<div>
  <h1></h1>
  <h2></h2>
  <p></p>
  <p></p>
  <p></p>
  just some text
</div>
*/
第 4 段(可获 0.3 积分)

字符串被转换为Text节点,因此不会解析任何HTML:

parent.append('<p>foo</p>')
parent.outerHTML
/* Outputs:
<div>
  <h1></h1>
  <h2></h2>
  <p></p>
  <p></p>
  <p></p>
  just some text
  &lt;p&gt;foo&lt;/p&gt;
</div>
*/

这与jQuery方法相反,该方法解析字符串标记,并生成相应的节点并插入到DOM树中。

通常情况下,如果附加节点已经存在于树中,则首先将其从旧位置移除:

const myParent = document.createElement('div')
const child = document.createElement('h1')
myParent.append(child)
const myOtherParent = document.createElement('div')
const myOtherParent.append(child)
myOtherParent.outerHTML
// <div><h1></h1></div>

myParent.outerHTML
// <div></div>"
第 5 段(可获 0.8 积分)

append()和appendChild()之间的最后一个区别是后者返回附加的节点,而前者返回undefined。

2. prepend()

prepend方法与append()方法非常相似。 但是这一次,子节点将被添加 到调用该方法的节点的子节点之前,在第一个子节点之前:

const parent = document.createElement('div')
const child1 = document.createElement('p')
parent.prepend(child1)
parent.outerHTML
// <div><p></p></div>

const child2 = document.createElement('h2')
parent.prepend('just some text', child2)
parent.outerHTML
/* Outputs:
<div>
  just some text
  <h2></h2>
  <p></p>
</div>
*/
第 6 段(可获 0.68 积分)

方法的返回值是 undefined. 相应的jQuery方法是 prepend().

3. after()

after方法是另一种插入方法,但是这一次它必须在子节点上调用,也就是一个具有父节点的节点。 节点作为相邻的兄弟节点插入,如以下示例所示:

const parent = document.createElement('ul')
const child = document.createElement('li')
child.append('First item')
parent.append(child)
child.after(document.createElement('li'))
parent.outerHTML
// <ul><li>First item</li><li></li></ul>
第 7 段(可获 0.69 积分)

该方法的返回值是undefined ,在jQuery中类似的操作是 after().

4. before()

before 方法与after()方法类似,但是现在节点被插入到子节点之前:

const parent = document.createElement('ul')
const child = document.createElement('li')
child.append('First item')
parent.append(child)

const child1 = document.createElement('li')
child1.append('Second item')

const child2 = document.createElement('li')
child2.append('Third item')

child.before(child1, child2)

parent.outerHTML
/* Outputs:
<ul>
  <li>Second item</li>
  <li>Third item</li>
  <li>First item</li>
</ul>
*/
第 8 段(可获 0.38 积分)

该方法的返回值也是undefined. 对应的 jQuery 方法是 before().

5. replaceWith()

假设我们想用另一个节点替换一个DOM节点。 当然,他们可能包含子节点,所以这个操作将替代整个DOM子树。 在介绍这套方便的方法之前,我们将使用replaceChild():

const parent = document.createElement('ul')
parent.innerHTML = `
  <li>first</li>
  <li>second</li>
  <li>third</li>
`
parent.outerHTML
// <ul><li>first</li><li>second</li><li>third</li></ul>"

const secondChild = parent.children[1]

const newSecondChild = document.createElement('li')
newSecondChild.innerHTML = '<a href="#">second</a>'

secondChild.parentNode.replaceChild(newSecondChild, secondChild)
parent.outerHTML
/* Outputs:
<ul>
  <li>first</li>
  <li><a href="#">second</a></li>
  <li>third</li>
</ul>
*/
第 9 段(可获 0.66 积分)

(innerHTML和模板文字用于减少DOM树的构造)

可以使用 replaceWith 以更简洁的方式执行相同的操作:

parent = document.createElement('ul')
parent.innerHTML = `
  <li>first</li>
  <li>second</li>
  <li>third</li>
`
secondChild = parent.children[1]

newSecondChild = document.createElement('li')
newSecondChild.innerHTML = '<a href="#">second</a>'

secondChild.replaceWith(newSecondChild)

该方法除了语法较短之外,还有一个特点是它接受几个参数,允许用其他节点列表替换一个节点。 继续之前的交互式JavaScript会话:

第 10 段(可获 0.75 积分)
const newerSecondChild = document.createElement('li')
newerSecondChild.append('another item')
const newThirdChild = document.createElement('li')
newThirdChild.append('yet another item')
newSecondChild.replaceWith(newerSecondChild, newThirdChild)
parent.outerHTML
/* Outputs:
<ul>
  <li>first</li>
  <li>another item</li>
  <li>yet another item</li>
  <li>third</li>
</ul>
*/

该方法的返回值也是undefined.。你可以将该方法与 jQuery的同音异义方法作比较。

remove()

从DOM树中删除节点怎么办? '老'方法是removeChild()。 如其名称所示,它必须在要删除的节点n的父节点上调用:

第 11 段(可获 0.63 积分)
n.parentNode.removeChild(n)

然而,使用remove()方法,操作起来相当简单:

const parent = document.createElement('ul')
const n = document.createElement('li')
parent.append(n)
parent.outerHTML
// <ul><li></li></ul>

n.remove()
parent.outerHTML
// <ul></ul>

与jQuery中类似操作的不同之处在于如何处理附加到已删除节点的事件监听器。 jQuery删除与元素相关联的所有绑定事件和数据,而原生方法不会涉及事件监听器:

const parent = document.createElement('ul')
const n = document.createElement('li')
parent.append(n)

n.addEventListener('test', console.log.bind(console))

const e = new Event('test')
n.dispatchEvent(e)
Event {isTrusted: false, type: "test", ...

n.remove()
n.dispatchEvent(e)
Event {isTrusted: false, type: "test", ...
第 12 段(可获 0.61 积分)

这种行为更类似于jQuery的detach方法。

浏览器支持

在撰写本文时,桌面浏览器上前五个方便方法 prepend()append()before()after() 和replaceWith()支持状态如下所示:

  • Chrome从54版本开始实现它们
  • Firefox从版本49开始支持它们
  • Safari从10版开始支持它们
  • Opera 从版本41开始支持他们。
  • 令人失望的是,它们在Internet Explorer中不支持,也不支持Microsoft Edge(尽管对于Edge,该功能正在开发中

remove方法得到了更广泛的支持。 Microsoft Edge从版本14开始实现它。

第 13 段(可获 1.15 积分)

对于那些还没有提供这些方法的浏览器,,有几种polyfills 是可用的. childNode.js 就是其中之一, 还有其他的polyfills可以在致力于这些方法的MDN 页面中找到, 在本文中已经提到过了。

7. 额外的方法: insertAdjacentHTML

在结束之前, 说一下insertAdjacentHTML。它提供了与上面列出的前四种方法类似的插入操作。 — append(), prepend(), after(), before() —用HTML字符串指定要添加的内容:

const parent = document.createElement('div')
parent.insertAdjacentHTML('beforeend', '<p>A paragraph</p>')
parent.insertAdjacentHTML('beforeend', '<p>Another paragraph</p>')
parent.insertAdjacentHTML('afterbegin', '<p>Yet another paragraph</p>')

const grandParent = document.createElement('div')
grandParent.append(parent)

parent.insertAdjacentHTML('afterend', '<div class="after"></div>')
parent.insertAdjacentHTML('beforebegin', '<div class="before"></div><div class="before2"></div>')

grandParent.outerHTML
/* Outputs:
<div>
  <div class="before"></div>
  <div class="before2"></div>
  <div>
    <p>Yet another paragraph</p>
    <p>A paragraph</p>
    <p>Another paragraph</p>
  </div>
  <div class="after"></div>
</div>
*/
第 14 段(可获 0.93 积分)

注意我们如何使父节点(parent)成为另一个节点的子节点,以便能够使用beforebegin和afterend位置参数。

幸运的是,insertAdjacentHTML()方法受到浏览器的广泛支持

结论

现在我们即将结束简要概述这些由jQuery启发的DOM方法。 我希望在本文中,我已经演示了原生DOM API的处理过程,以及这些原生方法如何简单地替换它们的jQuery对等方法。

但是您觉得呢? 这是否会破坏您的jQuery依赖关系? 还是缺乏IE支持? 我很想在下面的评论中收到您的回复。

第 15 段(可获 1.38 积分)

文章评论