通过上一节的学习,对JavaScript中的DOM有了一定的认识。虽然对DOM中相关的知识点有一定的概念,但还是缺乏对DOM的实际操作。如果你仔细阅读过上一篇文章的话,你应该会发现,当时也提到了一些DOM操作相关的东西,比如,DOM的增、删、改和查等。那么今天我们就来看看这些方面的东西。
DOM的增
先来看DOM操作中的增。其主要分为两个部分:新创建节点和插入节点。
新创建节点
常用的DOM节点创建有关的API接口主要有:
document.createElement
:创建指定的HTML元素或一个HTMLUnknownElement
document.createTextNode
:创建文本节点document.createDocumentFrame
:创建文档片段document.createAttribute
:创建节点属性document.adoptNode
:从外部文档中获取一个节点document.importNode
:拷贝外部文档的一个节点node.cloneNode
:克隆节点
document.createElement
document.createElement(tagName[, options])
是其中最常用的DOM API之一,主要用来创建由标签名称(tagName
)指定的HTML元素,如果标签名称不是一个有效的HTML元素,将会创建一个HTMLUnknownElement
对象。来看一个简单的示例:
let newEle = document.createElement('div');
let newContent = document.createTextNode('我是一个新创建的div元素')
newEle.appendChild(newContent)
document.body.appendChild(newEle)
注意,通过document.createElement
创建的元素并不属于document
对象,它只是创建出来,并未添加到HTML文档中,需要调用appendChild()
或insertBefore()
等方法将其添加到HTML文档中。
如果你对
HTMLUnknownElement
从未接触,建议你有空花点时间阅读@张鑫旭老湿的《》和@米粽大大翻译的《Custom Elements》。
document.createTextNode
document.createTextNode(text)
创建一个文本节点,参数text
为文本节点的内容。比如:
let newContent = document.createTextNode('我是一个新创建的div元素')
newEle.appendChild(newContent)
创建了一个文本节点newContent
,然后把这个新创建的文本节点通过appendChild()
方法,将其插入到newEle
元素中,当作其内容。document.createTextNode(text)
方法返回的节点,被浏览器当作文本渲染,而不是当作HTML代码渲染,因此会对HTML代码进行转义,可以用来展示用户的输入,避免XSS
攻击。
function escapeUserInput(str) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(str));
return div.innerHTML;
}
var userInput = '<p>危险内容</p>';
var template = '<div>' + escapeUserInput(userInput) + '</div>'
// 此时被转义,危险内容不再危险
<div><p>危险内容</p></div>
但是,该方法不对单引号和双引号转义,因此用来为属性赋值的时候,仍然会被 XSS
攻击:
var userInput = '" onmouseover="console.log(\'危险操作\')""';
var template = '<div color="' + escapeUserInput(userInput) + '">user set color</div>'
// 被注入一个 onmouseover 操作
<div color="" onmouseover="console.log('危险操作')""">user set color</div>
其中XSS
攻击属于Web安全方面的知识了,不属于这篇文章的范畴。如果你对XSS
相关的东西感兴趣的话,可以看看下面几篇文章:
document.createDocumentFragment
document.createDocumentFragment()
方法创建一个新空白的DocumentFragment
对象。
DocumentFragments
是DOM节点。它们不是主DOM树的一部分。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有的子元素代替。
因为文档片段存在于内存中,并不在DOM树中,所以将子元素插入到文档片段时不会引起页面回流(reflow
)(对元素位置和几何上的计算)。因此,使用文档片段document fragments 通常会起到优化性能的作用。
比如下面这个示例,给一个ul
添加10000
个li
,先用拼接字符串的方式来实现:
let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')
document.body.appendChild(newUlEle)
for (let i = 0; i < 10000; i++) {
str += '<li>第' + i + '个子节点</li>'
}
newUlEle.innerHTML = str
console.log('耗时' + (Date.now() - start) + 'ms');
多次刷新,可以看到创建10000
个li
时,渲染所需要的时间如下图:
把上面的示例,换成append()
的方式,逐个添加对应的li
:
let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')
document.body.appendChild(newUlEle)
for (let i = 0; i < 10000; i++) {
let liEle = document.createElement('li')
liEle.textContent = '第' + i + '个子节点'
newUlEle.appendChild(liEle)
}
console.log('耗时:' + (Date.now() - start) + 'ms')
这种方法所费时间如下图:
都说第二种方法要比第一种方法耗时,看上去有点像。接下来再来看createDocumentFragment
的方法。可以预见的是,这种方法肯定比第二种强,但应该没有第一种快:
let start = Date.now()
let str = ''
let newUlEle = document.createElement('ul')
document.body.appendChild(newUlEle)
let fragment = document.createDocumentFragment()
for (let i = 0; i < 10000; i++) {
let liEle = document.createElement('li')
liEle.textContent = '第' + i + '个子节点'
fragment.appendChild(liEle)
}
newUlEle.appendChild(fragment)
console.log('耗时:' + (Date.now() - start) + 'ms')
document.createAttribute()
document.createAttribute(attrName)
方法创建并返回一个新的属性节点。这个方法不是很常用,因为添加属性通常使用node.setAttribute()
。
let node = document.getElementById('content')
let attr = document.createAttribute('title')
attr.nodeValue = 'Hello JavaScript!'
node.setAttributeNode(attr)
上面的代码会给div#content
的元素添加一个title
属性,而且这个title
属性的值为Hello JavaScript!
:
同样的,document.createAttribute()
虽然创建了属性节点,如果不通过setAttributeNode()
方法的话,创建的属性的节点是不会运用到对应的元素节点上的。该方法的返回值是一个Attr
类型的节点。借助nodeValue
给该节点赋值,然后给该属性节点设置对应的属性值。等同的效果,常常使用setAttribute()
方法来替代该方法。后续我们会介绍到setAttribute()
方法相关的知识。
document.adoptNode
document.adoptNode(externalNode)
从其他的 document
中获取一个节点(externalNode
),并将该节点以及它的所有子节点从原文档删除, 并且它的 ownerDocument
属性会变成当前的 document
。之后你可以把这个节点插入到当前文档中,不常用,了解即可。
// 该函数用来从本文档的第一个 iframe 中获取第一个 element 元素,
// 并插入到当前文档树中
function getEle(){
var iframe = document.getElementsByTagName("iframe")[0],
ele = iframe.contentWindow.document.body.firstElementChild;
if(ele){
document.body.appendChild(document.adoptNode(ele))
}else{
alert("没有更多元素了")
}
}
document.getElementById("move").onclick = getEle
注意,该方法在同一 document
下的不同两个元素中也可以使用,可以实现从左边栏列表中选取某些元素加载到右边栏的功能。如果节点资源来自不同的源的时候,调用 adoptNode
可能会失败。
有些情况下,将外部文档的节点插入当前文档之前,你需要使用 document.importNode()
从外部文档导入源节点,了解更多细节。
document.importNode
document.importNode(externalNode, deep)
这个接口也不常用,作用是拷贝外部文档的一个节点(externalNode
)。deep
表明是否要导入节点的后代节点,默认为 false
不导入后代节点。
var iframe = document.getElementsByTagName("iframe")[0];
var oldNode = iframe.contentDocument.getElementById("myNode");
var newNode = document.importNode(oldNode, true);
document.getElementById("container").appendChild(newNode);
注意,这个方法仅拷贝节点,此时,节点存在于内存中,还需要插入当前文档中才能显示。
node.cloneNode
node.cloneNode(deep)
方法返回该节点的一个副本,deep
可选,表明是否采用深度克隆,如果为true
,则该节点的所有后代节点也都会被克隆,否则,只克隆该节点本身。
let node = document.getElementById('content')
let cloneNode = node.cloneNode(true)
cloneNode.id = "newId"
document.body.appendChild(cloneNode)
上面的这个小示例,克隆了div#content
以及其所有后代节点,并且把新克隆的元素的id
赋值为newId
,然后再把新克隆出来的所有节点重新插入body
中。最终的效果如下:
上面的示例,演示了,使用node.cloneNode(true)
可以克隆节点的所有后代节点以及其所有属性。那么对于绑定的事件是否也能被克隆呢?还是通过示例来验证一下,看看事件是否也会被克隆。
<div id="box">
<button id="clone" onclick="console.log('Click Clone Button')">Clone Me!</button>
</div>
<div id="new"></div>
// cloneNode
let btn = document.getElementById('clone')
let box = document.getElementById('box')
let newDiv = document.getElementById('new')
newDiv.appendChild(box.cloneNode(true))
上面的示例使用了内联方式直接把事件写在HTML标签上。从结果我们可以看到绑定在HTML标签上的事件也被克隆了。
接下来在上例的基础上做一下调整,把内联方式换成绑定在节点对象上的事件:
let btn = document.getElementById('clone')
let box = document.getElementById('box')
let newDiv = document.getElementById('new')
btn.onclick = function () {
console.log('click clone')
}
newDiv.appendChild(box.cloneNode(true))
从结果可以看出,绑定在节点对象的事件在克隆的副本并不包含事件处理程序。接着再做一下调整,使用addEventListener()
方法把事件添加在节点上:
btn.addEventListener('click', function (){
console.log('Click clone!')
})
得到的效果其实和上图是一样的。也就是说,克隆的时候,addEventListener()
绑定的事件并没有被克隆。
从上面的示例可以证明,副本节点只能绑定使用内联方式绑定的事件处理函数。简单点说,只有内联在HTML元素的事件,才会被
cloneNode()
克隆。
注意,这个拷贝的节点并不在文档中,需要自行添加到文档中。同时拷贝的节点有可能会导致节点的的 id
属性重复,最好修改新节点的 id
,而 name
属性也可能重复,自行决定是否需要修改。
节点修改
DOM节点修改有关的API有:
node.appendChild()
:插入一个新节点node.insertBefore()
:插入一个新节点node.removeChild()
:删除一个节点node.replaceChild()
:替换一个节点
其中node.appendChild
和node.insertBefore
属于DOM增中的新节点插入,而removeChild
属于DOM中的删,replaceChild
属于DOM中的改。这一节,咱们只先聊增这一部分,对于删和改,我们后面会单独介绍。
node.appendChild
parentNode.appendChild(child)
方法将一个节点child
添加到指定的父节点parentNode
的子节点列表的末尾。本方法返回值为要插入的这个节点。
let pEle = document.createElement('p')
pEle.textContent = '我是新添加的p元素'
document.body.appendChild(pEle)
上面的示例创建了一个新的段落元素pEle
,然后使用appendChild()
将这个新创建的元素添加到body
的最末尾。
使用appendChild()
方法的时候有一点需要注意。如果被插入的节点已经存在文档树中,则节点会被从原先的位置移除,并插入到新的位置。当然,被移动的元素被绑定的事件也会被同步过去,比如:
<div id="old">
<p id="move">我是一个段落元素</p>
</div>
<div id="new"></div>
<button id="btn">创建元素</button>
let pEle = document.getElementById('move')
let newEle = document.getElementById('new')
let btnEle = document.getElementById('btn')
pEle.addEventListener('click', function() {
console.log('click me!')
})
btnEle.addEventListener('click', function () {
pEle.textContent = '我是新添加的p元素'
newEle.appendChild(pEle)
})
如果要保留原来的这个子节点的位置,则可以用 Node.cloneNode
方法复制出一个节点的副本,然后再插入到新位置。这个方法只能将某个子节点插入到同一个文档的其他位置,如果你想跨文档插入,需要先调用document.importNode
方法。还有,如果appendChild()
方法的参数是DocumentFragment
节点,那么插入的是DocumentFragment
的所有子节点,而不是DocumentFragment
节点本身。此时,返回值是一个空的DocumentFragment
节点。
node.insertBefore
parentNode.insertBefore(child, referenceNode)
方法将一个节点child
插入作为父节点parentNode
的一个子节点,并且位置在参考节点referenceNode
之前。
如果第二个参数referenceNode
为null
,则插入位置你父节点的末尾:
parentNode.insertBefore(node, null);
// 等价于
parentNode.appendChild(node);
注意,第二个参数为
null
时不能省略,否则会报错。
来看一个小示例:
<div id="parent">
我是父节点
<p id="child">我是旧的子节点</p>
</div>
<button id="btn">插入节点</button>
let parentEle = document.getElementById('parent')
let childEle = document.getElementById('child')
let btnEle = document.getElementById('btn')
btnEle.addEventListener('click', function () {
let newEle = document.createElement('span')
newEle.textContent = '我是新添加节点的文本内容'
parentEle.insertBefore(newEle, childEle)
})
使用这个方法可以模拟prependChild
,产生类似于appendChild()
,但是将节点插入作为指定父节点的第一个子节点:
Node.prototype.prependChild = function (node) {
return this.insertBefore(node, this.firstChild)
}
let parentEle = document.getElementById('parent')
let btnEle = document.getElementById('btn')
btnEle.addEventListener('click', function () {
let newEle = document.createElement('p')
newEle.textContent = '我是新添加节点的文本内容'
parentEle.prependChild(newEle)
})
其实这个效果和前面的效果是类似的。同样的,使用这个方法还可以模拟insertAfter
,将节点要插在父节点的某个子节点后面:
Node.prototype.insertAfter = function(node, referenceNode) {
return this.insertBefore(node, referenceNode.nextSibling);
}
和 appendChild
类似,如果插入的节点是文档中已经存在的节点,则会移动该节点到指定位置,并且保留其绑定的事件。
DOM的删
DOM节点的删除主要API是node.removeChild
。可以使用parentNode.removeChild(child)
删除指定父节点parentNode
的一个子节点child
,并返回被删除的节点。
这个方法是要在被删除的节点的父节点上调用的,而不是在被删除节点上调用的,如果参数节点不是当前节点的子节点,removeChild
方法将报错:
// 通过 parentNode 属性直接删除自身
var node = document.getElementById('deleteDiv');
if (node.parentNode) {
node.parentNode.removeChild(node);
}
// 也可以封装以下作为一个方法直接使用:
Node.prototype.remove = function(node) {
if (node.parentNode) {
return node.parentNode.removeChild(node);
}
throw new Error('Can not delete.');
}
node.remove();
使用这个方法也可以很简单的模拟 removeAllChild
:
Node.prototype.removeAllChild = function() {
var deleteNode = []
while (this.firstChild) {
deleteNode.push(this.removeChild(this.firstChild));
}
return deleteNode;
}
被移除的这个子节点仍然存在于内存中,只是不在当前文档的 DOM 中,仍然还可以被添加回文档中。但是如果不使用一个变量保存这个节点的引用,被删除的节点将不可达,会在某次垃圾回收被清除。
DOM的改
parentNode.replaceChild(newChild, oldChild)
方法用指定的节点newChild
替换当前节点parentNode
的一个子节点oldChild
,并返回被替换的节点oldChild
。
<div id="parent">
<p id="child">我是旧的第一个子节点</p>
</div>
<button id="btn">替换节点</button>
let parentEle = document.getElementById('parent')
let oldEle = document.getElementById('child')
let btnEle = document.getElementById('btn')
btnEle.addEventListener('click', function () {
let newEle = document.createElement('p')
newEle.setAttribute('id', 'newChild')
newEle.textContent = '我是新添加节点的文本内容'
parentEle.replaceChild(newEle, oldEle)
})
简单的总结一下
DOM中的节点操作对应的主要API有:
appendChild()
:用于向childNodes
列表的末尾添加一个节点。返回新增的节点。insertBefore()
:接收两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点。同时被方法返回。replaceChild()
:接收两个参数:要插入的节点和要替换的节点。要替换的节点将由这个方法返回并从文档树中移除。同时由要插入的节点占据其位置。removeChild()
:接收一个参数,即要移除的节点。返回被移除的节点。
这四个方法都是操作的某个节点的子节点,也就是说,要使用这几个方法必须先取得父节点。另外并不是所有节点都有子节点,如果在不支持子节点的节点上,调用了这些方法,将会导致错误。
DOM的查
DOM节点中的查主要包括:查找元素(类似于CSS中的选择器)和节点查找。
查找元素
先来看DOM中怎么查找到元素,也就是说选择到你想要的元素。在DOM中查找元素(选择到想要的元素)对应的API主要有:
document.getElementById(id)
:匹配特定id
的元素document.getElementsByName(name)
:根据给定的name
返回一个在 (X)HTMLdocument
的NodeList
集合document.getElementsByTagName(tagName)
:返回一个包括所有给定标签名称的元素的HTML集合HTMLCollection
document.getElementsByClassName(className)
:返回包含了所有指定类名的子元素的类数组对象document.querySelector(selector)
:返回文档中与指定选择器或选择器组匹配的第一个Element
document.querySelectorAll(selector)
:返回与指定的选择器组匹配的文档中的元素列表。返回的对象是NodeList
假设我们有一个简单的DOM文档:
<div id="box">
<h3>Title</h3>
<ul class="list">
<li class="item">Item1</li>
<li class="item">Item2</li>
<li class="item">Item3</li>
<li class="item">Item4</li>
<li class="item">Item5</li>
</ul>
<p id="intro">Intro ...</p>
</div>
为了更好的说明前面的几个API,后续中的示例,都会采用这个DOM结构。其对应的DOM树不再绘制了。
document.getElementById(id)
document.getElementById(id)
返回的是一个Element
对象,用来匹配文档中指定的id
元素。如果没有找到对应的元素,该方法会返回null
。另外,document.getElementById()
方法不会搜索不在文档中的元素。当创建一个元素,并且分配id
后,必须要使用insertBefore
或其他类似的方法把元素插入到文档之后才能使用document.getElementById()
获取到。
来看一个示例:
let idEle = document.getElementById('intro')
let btnEle = document.getElementById('btn')
btnEle.addEventListener('click', function () {
console.log(`能匹配到的: ${idEle}`)
console.log(idEle)
let newEle = document.createElement('section')
newEle.id = 'main'
newEle.textContent = '我是新添加的元素'
console.log(`未插入到DOM的新元素newEle: ${document.getElementById('main')}`)
console.log(document.getElementById('main'))
let box = document.getElementById('box')
box.insertBefore(newEle, idEle)
console.log(`插入到DOM的新元素newEle: ${document.getElementById('main')}`)
console.log(document.getElementById('main'))
})
来看输出的结果:
比如上面示例,通过document.getElementById()
之后,咱们获取了DOM上的节点,这个时候可以对该节点做很多事情,比如查询内容和属性,或者其他任何操作,甚至可以删除它,克隆它,或者将它移动到DOM树的其它节点上。
注意,document.getElementById(id)
中的id
参数是有大小写敏感的,所以document.getElementById('Intro')
无法获取到元素<p id="intro">Intro ...</p>
。另外还有就是,如果文档中有多个相同的id
(这种情形一般不存在)时,只会返回第一个。
document.getElementsByName(name)
document.getElementsByName(name)
将根据给定的name
返回一个在document
的节点列表集合。name
属性只有在HTML文档中可用。该方法返回的是一个NodeList
集合,这个集合包含name
属性为指定值的所有元素,比如<meta>
、<object>
,甚至那些不支持name
属性但是添加了name
自定义属性的元素也包含其中。
该方法常用于取得单选按钮。同样也会返回HTMLCollection
对象。HTMLCollection
对象可以通过length
属性访问元素长度,通过[]
方括号语法访问对象中的项。方括号中既可以是数字,也可以是字符串索引值。
document.getElementsByTagName(tagName)
document.getElementsByTagName(tagName)
将会返回一个包括所有给定标签名称tagName
的元素的HTML集合HTMLCollection
。整个文件结构都会被搜索,包括根节点。返回的HTML集合是动态的,意味着它可以自动更新来保持和DOM树同步,而不用再次调用document.getElementsByTagName(tagName)
。
let liEle = document.getElementsByTagName('li')
let btnEle = document.getElementById('btn')
btnEle.addEventListener('click', function () {
console.log(`能匹配到的: ${liEle}`)
console.log(liEle.length)
Object.keys(liEle).forEach(key => {
console.log(key, liEle[key])
})
})
比如上面的示例,通过getElementsByTagName('li')
获取了文档中所有的<li>
元素。其开始于一个具体的父元素并且从它自上而下递归地在DOM树中搜索符合标签名称参数的子元素。刚才也说了,其返回的是一个动态的HTMLCollection
对象。获得这个对象之后,可以对其做一些遍历操作。比如上面使用Object.keys()
遍历出li
:
有关于JavaScript中对象遍历相关的操作可以阅读《如何遍历JavaScript中对象属性》和《对象属性的枚举》。
有一点需要注意,调用 getElementsByTagName()
的不是那个文件节点 document
,事实上是使用这个方法 element.getElementsByTagName()
。
document.getElementsByClassName(className)
document.getElementsByClassName(className)
返回一个包含了所有指定类名的子元素的类数组对象。当在document
对象上调用时,会搜索整个DOM文档,包含根节点。你也可以在任意元素上调用getElementsByClassName()
方法,它将返回的是以当前元素为根节点,所有指定类名的子元素。
比如,获取所有class
为item
的元素:
document.getElementsByClassName('item')
如果你想获取多个class
的元素时,可以用空格来隔开,比如说,同时获取所有class
同时包括btn
和btn-lg
的元素:
document.getElementsByClassName('btn btn-lg')
如果你想获取某个元素的子节点中对应class
的元素时,你也可以像下面这样操作:
document.getElementById('box').getElementsByClassName('item')
document.querySelector(selector)
document.querySelector(selector)
方法可以帮助你选择一个HTML元素。如果选择了多个HTML元素,其总是返回第一个元素。它看起来像这样:
document.querySelector('li')
使用这个方法可以通过id
、class
以及标签元素,甚至是元素的一些属性可以选择一个元素。
- 使用一个
id
选择元素,需要在id
前使用#
- 使用一个
class
选择元素,需要在class
前使用.
- 使用一个标签选择元素,可以直接把元素标签当作选择器
甚至为了更好的理解或者记忆,只要满足CSS的选择器,那么都可以被运用于document.querySelector(selector)
中的selector
选择器。
document.querySelectorAll(selector)
document.querySelectorAll(selector)
可以帮助你选择多个元素。这个方法中的selector
和document.querySelector()
具有相同的语法。唯一不同的是,你可以通过用逗号,
分隔来选择多个元素。
比如:
var matches = document.querySelectorAll("div.note, div.alert");
节点查找
DOM中节点共有12种类型,每种类型分别表示文档中不同的信息标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。节点之间的关系构成了层次,而所有页面标记则表现为一个以特定节点为根节点的树形结构。用张图来描述:
所有的节点都有这些属性,都是可以用于访问相关的node
节点:
Node.childNodes
: 访问一个单元素下所有的直接子节点元素,可以是一个可循环的类数组对象。该节点集合可以保护不同的类型的子节点(比如text节点或其他元素节点)。Node.firstChild
: 与childNodes
数组的第一个项(Element.childNodes[0]
)是同样的效果,仅仅是快捷方式。Node.lastChild
: 与childNodes
数组的最后一个项(Element.childNodes[Element.childNodes.length-1]
)是同样的效果,仅仅是快捷方式。Node.parentNode
: 访问当前节点的父节点,父节点只能有一个,祖节点可以用Node.parentNode.parentNode
的形式来访问。Node.nextSibling
: 访问DOM树上与当前节点同级别的下一个节点。Node.previousSibling
: 访问DOM树上与当前节点同级别的上一个节点。
用张图来阐述,会更清晰:
通过这张图,理解起来就简单多了,但有个非常重要的知识点:那就是元素之间不能有空格,如果ul
和li
之间有空格的话,就会被认为是内容为空的text node
节点,这样ul.childNodes[0]
就不是第一个li
元素了。相应地,<p>
的下一个节点也不是<ul>
,因为<p>
和<ul>
之间有一个空行的节点,一般遇到这种情况需要遍历所有的子节点然后判断nodeType
类型。
根据上面的描述,我们可以把DOM中的节点相互之间存在着的各种关系分为:父子关系,兄弟关系等:
父关系相关的API
parentNode
:每个节点都有一个parentNode
属性,它表示元素的父节点。Element
的父节点可能是Element
,Document
或DocumentFragment
;如果不存在,则返回null
parentElement
:返回元素的父元素节点,与parentNode
的区别在于,其父节点必须是一个Element
元素,如果不是,则返回null
;
子关系API
children
:返回一个实时的HTMLCollection
,子节点都是Element
;保存的是该节点的第一层元素子节点childNodes
:返回一个实时的NodeList
,表示元素的子节点列表,注意子节点可能包含文本节点、注释节点等;firstChild
:返回第一个子节点,不存在返回null
,与之相对应的还有一个firstElementChild
;lastChild
:返回最后一个子节点,不存在返回null
,与之相对应的还有一个lastElementChild
;
兄弟关系型API
previousSibling
:节点的前一个节点,如果不存在则返回null
。注意有可能拿到的节点是文本节点或注释节点,与预期的不符,要进行处理一下。nextSibling
:节点的后一个节点,如果不存在则返回null
。注意有可能拿到的节点是文本节点,与预期的不符,要进行处理一下。previousElementSibling
:返回前一个元素节点,前一个节点必须是Element
。nextElementSibling
:返回后一个元素节点,后一个节点必须是Element
。
总结
DOM操作在JavaScript还是很重要的,简单点说,所有的交互操作都是基于DOM来操作的。而DOM中的操作,最为熟悉的就是对DOM的增、删、改、查。今天的内容也就围绕着这几个方面展开。因为涉及到的内容较多,可能会有遗漏或者说零乱。如果你觉得上面不对之处,还请路过大神指正。
如需转载,烦请注明出处:https://www.w3cplus.com/javascript/operate-dom.html