DOM操作
Node
是一个接口,各种类型的 DOM API 对象会从这个接口继承。它允许我们使用相似的方式对待这些不同类型的对象;比如, 继承同一组方法,或者用同样的方式测试。
这里我们不谈其属性,只关注Node
节点的操作。
Node.appendChild()
**Node.appendChild(aChild)
**方法将一个节点附加到指定父节点的子节点列表的末尾处。
值得注意的是:
- 该方法只接受一个参数
- 其参数必须是一个
Node
,不能接受DOMString
对象 - 如果
aChild
已存在文档中,则该方法会先remove该节点,再在目标位置插入节点 - 其会返回插入的子节点(
aChild
)
兼容性如下,可以说是最起始一部分操作方法了。
ParentNode.append()
ParentNode.append
方法在 ParentNode
的最后一个子节点之后插入一组 Node
对象或 DOMString
对象。
被插入的 DOMString
对象等价为 Text
节点。
与appendChild
不同的是:
- 允许追加 DOMString 对象,而 Node.appendChild() 只接受 Node 对象。
- 没有返回值,而 Node.appendChild() 返回追加的 Node 对象。
- 可以追加多个节点和字符串,而 Node.appendChild() 只能追加一个节点。
这个方法的兼容性相对差一些,但是功能全面。
Node.insertBefore()
Node.insertBefore()
方法在参考节点之前插入一个拥有指定父节点的子节点。函数返回被插入过的子节点。
注意:
- 如果给定的子节点是对文档中现有节点的引用,
insertBefore()
会将其从当前位置移动到新位置
语法
1 | var insertedNode = parentNode.insertBefore(newNode, referenceNode); |
insertedNode
被插入节点(newNode)parentNode
新插入节点的父节点newNode
用于插入的节点referenceNode
newNode
将要插在这个节点之前
如果 referenceNode
为 null
则 newNode
将被插入到子节点的末尾。
兼容性
Node.removeChild
Node.removeChild()
方法从DOM中删除一个子节点。返回删除的节点。
语法
1 | let oldChild = node.removeChild(child); |
child
是要移除的那个子节点.node
是child
的父节点.- oldChild保存对删除的子节点的引用.
oldChild
===child
.
注意:
被移除的这个子节点仍然存在于内存中,只是没有添加到当前文档的DOM树中,因此,你还可以把这个节点重新添加回文档中,当然,实现要用另外一个变量比如
上例中的oldChild
来保存这个节点的引用. 如果使用上述语法中的第二种方法, 即没有使用 oldChild 来保存对这个节点的引用, 则认为被移除的节点已经是无用的, 在短时间内将会被内存管理回收.
Node.replaceChild()
Node.replaceChild()
方法用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点。
语法
1 | let node = parentNode.replaceChild(newChild, oldChild); |
newChild
用来替换 oldChild
的新节点。如果该节点已经存在于 DOM 树中,则它首先会被从原始位置删除。
oldChild
被替换掉的原始节点。
兼容性
Node.cloneNode
**Node.cloneNode()
**方法返回调用该方法的节点的一个副本.
语法
1 | var dupNode = node.cloneNode(deep); |
node
将要被克隆的节点
dupNode
克隆生成的副本节点
deep
可选
是否采用深度克隆,如果为true,
则该节点的所有后代节点也都会被克隆,如果为false,则只克隆该节点本身.
注意: 在 DOM4 规范中(实现于Gecko 13.0(Firefox 13.0 / Thunderbird 13.0 / SeaMonkey 2.10)),
deep
是一个可选参数。如果省略的话,参数的默认值为true,
也就是说默认是深度克隆。如果想使用浅克隆, 你需要将该参数设置为false。
在最新的规范里,该方法的行为已经改变了,其默认值变成了 false。虽然该参数仍旧是可选的,但是你必须要为该方法设置 deep 参数,无论是为了向前还是向后兼容考虑。假如开发者没设置参数的话,Gecko 28.0 (Firefox 28 / Thunderbird 28 / SeaMonkey 2.25 / Firefox OS 1.3)) 版本的控制台会发出警告。从 Gecko 29.0 (Firefox 29 / Thunderbird 29 / SeaMonkey 2.26)) 开始该方法默认为浅复制而不是深度复制。
值得注意的是:
克隆一个元素节点会拷贝它所有的属性以及属性值,当然也就包括了属性上绑定的事件(比如
onclick="alert(1)"
),但不会拷贝那些使用addEventListener()
方法或者node.onclick = fn
这种用JavaScript动态绑定的事件。在使用
Node.appendChild()
或其他类似的方法将拷贝的节点添加到文档中之前,那个拷贝节点并不属于当前文档树的一部分,也就是说,它没有父节点。如果
deep
参数设为false
,则不克隆它的任何子节点.该节点所包含的所有文本也不会被克隆,因为文本本身也是一个或多个的Text
节点。兼容性
注意:为了防止一个文档中出现两个ID重复的元素,使用
cloneNode()方法克隆的节点在需要时应该指定另外一个与原ID值不同的ID
Node.contains
Node.contains()
返回的是一个布尔值,来表示传入的节点是否为该节点的后代节点。
语法
1 | node.contains( otherNode ) |
node
是否包含otherNode节点.otherNode
是否是node的后代节点.
如果 otherNode
是 node 的后代节点或是
node
节点本身.则返回true
, 否则返回 false
.
兼容性
Node.isEqualNode
**Node.isEqualNode()
**方法可以判断两个节点是否相等。当两个节点的类型相同,定义特征(defining characteristics)相同(对元素来说,即 id,孩子节点的数量等等),属性一致等,这两个节点就是相等的。一些具体的数据指出:多数时候的比较是根据节点的类型来的。
语法
1 | var isEqualNode = node.isEqualNode(otherNode); |
- otherNode: 比较是否相等的节点.
注意:这里的equal
与===
不同,用===
比较时,比较的是node的唯一id。而这里是鸭子比较法。
Node.hasChildNodes
hasChildNodes
方法返回一个布尔值,表明当前节点是否包含有子节点.
总结
有三种方法可以判断当前节点是否有子节点。
- node.firstChild !== null
- node.childNodes.length > 0
- node.hasChildNodes()
附加
可以使用element
标签的innerHTML
与innerText
属性。以及insertAdjacentHTML
方法来修改元素内部的值。
element.innerHTML
Element.innerHTML
属性设置或获取HTML语法表示的元素的后代。
注意:如果一个
语法
1 | const content = element.innerHTML; |
DOMString
包含元素后代的HTML序列。设置元素的 innerHTML
将会删除所有该元素的后代并以上面给出的 htmlString 替代。
注意:
SyntaxError
当 HTML 没有被正确标记时,设置 innerHTML
将会抛出语法错误。
NoModificationAllowedError
当父元素是 Document
时,设置 innerHTML
将会提示不允许修改。
安全问题
用 innerHTML
插入文本到网页中并不罕见。但这有可能成为网站攻击的媒介,从而产生潜在的安全风险问题。
1 | const name = "John"; |
尽管这看上去像 cross-site scripting 攻击,结果并不会导致什么。HTML 5 中指定不执行由 innerHTML
插入的script
标签。
然而,有很多不依赖<script>
标签去执行,innerHTML
去设置你无法控制的字符串时,这仍然是一个安全问题。例如:
1 | const name = "<img src='x' onerror='alert(1)'>"; |
于这个原因,当插入纯文本时,建议不要使用 innerHTML
。取而代之的是使用 Node.textContent
或者element.innerText
,它不会把给定的内容解析为 HTML,它仅仅是将原始文本插入给定的位置。
兼容性
HTMLElement.innerText
innerText
属性表示一个节点及其后代的“渲染”文本内容。与innerText
一样,其可以作为一个getter,其作为一个getter的时候,获取的内容与用户光标选中后复制的内容差不多
Note:
innerText
很容易与Node.textContent
混淆, 但这两个属性间实际上有很重要的区别. 大体来说,innerText
可操作已被渲染的内容, 而textContent
则不会.
Node.textContent
Node
接口的 textContent
属性表示一个节点及其后代的文本内容。
返回值
textContent
的值取决于具体情况:
- 如果节点是一个
document
,或者一个 DOCTYPE ,则textContent
返回null
。 - 如果节点是个
CDATA section
、注释、processing instruction
或者text node
,textContent
返回节点内部的文本内容,例如Node.nodeValue
。 - 对于其他节点类型,
textContent
将所有子节点的textContent
合并后返回,除了注释和processing instructions。(如果该节点没有子节点的话,返回一个空字符串。)
注意:在节点上设置 textContent
属性的话,会删除它的所有子节点,并替换为一个具有给定值的文本节点。
与 innerText 的区别
textContent
会获取所有元素的内容,包括<script>
和<style>
元素,然而innerText
只展示给人看的元素。textContent
会返回节点中的每一个元素。相反,innerText
受 CSS 样式的影响,并且不会返回隐藏元素的文本,- 此外,由于
innerText
受 CSS 样式的影响,它会触发回流( reflow )去确保是最新的计算样式。(回流在计算上可能会非常昂贵,因此应尽可能避免。)
- 此外,由于
- 与
textContent
不同的是, 在 Internet Explorer (小于和等于 11 的版本) 中对innerText
进行修改, 不仅会移除当前元素的子节点,而且还会永久性地破坏所有后代文本节点。在之后不可能再次将节点再次插入到任何其他元素或同一元素中。