JavaScript学习笔记:DOM的操作

如果看完了还不过瘾?想和作者深聊相关话题,可以点击这里向作者提问!

通过上一节的学习,对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>&lt;p&gt;危险内容&lt;/p&gt;</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添加10000li,先用拼接字符串的方式来实现:

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');

多次刷新,可以看到创建10000li时,渲染所需要的时间如下图:

把上面的示例,换成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.appendChildnode.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之前。

如果第二个参数referenceNodenull,则插入位置你父节点的末尾:

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)HTML documentNodeList集合
  • 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()方法,它将返回的是以当前元素为根节点,所有指定类名的子元素。

比如,获取所有classitem的元素:

document.getElementsByClassName('item')

如果你想获取多个class的元素时,可以用空格来隔开,比如说,同时获取所有class同时包括btnbtn-lg的元素:

document.getElementsByClassName('btn btn-lg')

如果你想获取某个元素的子节点中对应class的元素时,你也可以像下面这样操作:

document.getElementById('box').getElementsByClassName('item')

document.querySelector(selector)

document.querySelector(selector)方法可以帮助你选择一个HTML元素。如果选择了多个HTML元素,其总是返回第一个元素。它看起来像这样:

document.querySelector('li')

使用这个方法可以通过idclass以及标签元素,甚至是元素的一些属性可以选择一个元素。

  • 使用一个id选择元素,需要在id前使用#
  • 使用一个class选择元素,需要在class前使用.
  • 使用一个标签选择元素,可以直接把元素标签当作选择器

甚至为了更好的理解或者记忆,只要满足CSS的选择器,那么都可以被运用于document.querySelector(selector)中的selector选择器。

document.querySelectorAll(selector)

document.querySelectorAll(selector)可以帮助你选择多个元素。这个方法中的selectordocument.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树上与当前节点同级别的上一个节点。

用张图来阐述,会更清晰:

通过这张图,理解起来就简单多了,但有个非常重要的知识点:那就是元素之间不能有空格,如果ulli之间有空格的话,就会被认为是内容为空的text node节点,这样ul.childNodes[0]就不是第一个li元素了。相应地,<p>的下一个节点也不是<ul>,因为<p><ul>之间有一个空行的节点,一般遇到这种情况需要遍历所有的子节点然后判断nodeType类型。

根据上面的描述,我们可以把DOM中的节点相互之间存在着的各种关系分为:父子关系,兄弟关系等:

父关系相关的API

  • parentNode:每个节点都有一个parentNode属性,它表示元素的父节点。Element的父节点可能是ElementDocumentDocumentFragment;如果不存在,则返回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的增、删、改、查。今天的内容也就围绕着这几个方面展开。因为涉及到的内容较多,可能会有遗漏或者说零乱。如果你觉得上面不对之处,还请路过大神指正。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:https://www.w3cplus.com/javascript/operate-dom.html

如需转载,烦请注明出处:https://www.w3cplus.com/javascript/operate-dom.html

如果文章中有不对之处,烦请各位大神拍正。如果你觉得这篇文章对你有所帮助,打个赏,让我有更大的动力去创作。(^_^)。看完了?还不过瘾?点击向作者提问!

赏杯咖啡,鼓励他创作更多优质内容!
返回顶部