小雄回忆录(二)--DOM操作,原生js

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 = '" οnmοuseοver="console.log(\'危险操作\')" "';
var template = '<div color="' + escapeUserInput(userInput) + '">user set color</div>'

// 被注入一个 onmouseover 操作
<div color="" οnmοuseοver="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" οnclick="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)HTML document 的 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的增、删、改、查。今天的内容也就围绕着这几个方面展开。因为涉及到的内容较多,可能会有遗漏或者说零乱。如果你觉得上面不对之处,还请路过大神指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值