一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情
DOM节点操作
概述
通常获取元素通常使用的两种方式:
- 利用DOM提供的方法获取元素:
缺点是比较繁琐,逻辑性不强,就拿下面的来举例子,获取div要写一次,获取span要写一次,获取ul中的小li要先获取ul,然后再.quertSelectorAll('li')
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
</ul>
- 利用节点层级关系获取元素
- 利用父子兄节点关系获取元素
- 逻辑性强,但兼容性稍差
从DOM树来看,网页中所有的内容都是节点。元素就叫元素节点,文档就叫文档节点、还有属性节点、文本节点。一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)、nodeValue(节点值) 这三个基本属性
示例
var div = document.querySelector('div');
console.dir(div); //在里面可以找到节点的三个基本属性
元素节点 nodeType 为1
属性节点 nodeType 为2
文本节点 nodeType 为3(文本节点包含文字、空格、换行等)
1.父节点 parentNode
<div class="demo">
<div class="box1">
<div class="box2"></div>
</div>
</div>
正常情况,如果要操作box1和box2这两个盒子需要通过DOM提供的方法获取两次元素
var box1 = document.querySelector('.box1');
var box2 = document.querySelector('.box2');
...其他操作
可以看到我们获取了两次,但是我们可以通过节点操作简化代码:
var box2 = document.querySelector('.box2');
这时候可以通过 box2.parentNode; 来操作box2的父元素
注意:parentNode获取的是离该元素最近的父级节点
2.子节点操作
见如下结构:
<ul>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
</ul>
<ol>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
<li>我是小li</li>
</ol>
以前我们获取一些子元素是用DOM提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
现在利用节点操作:
(1) 父节点.childNodes
console.log(ul.childNodes); //NodeList(9)
ul中明明只有4个li,却得到9个元素,原因是childNodes能获取所有的子节点,包括元素节点、文本节点等(而文本节点包括空格换行和文字,这里li后面有换行,所以5个换行+4个li就是9个元素)
//测试
console.log(ul.childNodes[0].nodeType); //文本节点的节点类型为3
console.log(ul.childNodes[1].nodeType); //元素节点的节点类型为1
所以呢如果只想获得里面的元素节点,则需要专门处理。所以我们一般不提倡使用childNodes
专门处理:
for(let i = 0; i < ul.childNodes.length; i++) {
if(ul.childNodes[i].nodeType == 1){...其他操作}
}
(2) 父节点.children(非标准)
这是一个只读属性,返回所有的子元素节点。只返回子元素节点,其他的不返回 虽然这是一个非标准,但得到了各个浏览器的支持,可以随便使用
console.log(ul.children); //HTMLcollection(4) 是一个伪数组
(3) 父节点.firstChild、父节点.lastChild
(3)(4)见如下结构
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
var ol = document.querySelector('ol');
console.log(ol.firstChild); //#text 这是获取到了换行符,是文本类型的节点
console.log(ol.lastChild); //#text
firstChild、lastChild两个属性分别返回第一个子节点和最后一个子节点,找不到则返回null。与childNodes一样包含的是所有节点。
(4) 父节点.firstElementChild、父节点.lastElementChild
还是刚刚(3)中的结构
console.log(ol.firstElementChild); // <li>...</li>
console.log(ol.lastElementChild); // <li>...</li>
这两个属性分别返回第一个子元素节点和最后一个子元素节点,找不到则返回null,但是呢这两个方法有兼容性问题,IE9以上才支持。
提出问题: 实际开发中,firstChild和lastChild包含其他节点,操作不方便,而firstElementChild和lastElementChild又有兼容性问题,那么我们如何获取第一个子元素节点或者最后一个子元素节点呢
(2)中讲了利用父节点.children获取所有子元素节点 实际开发中可以这样写
console.log(ol.children[0]); //ol的第一个
console.log(ol.children[ol.children.length - 1]); //ol的最后一个
兄弟节点
<div>我是 div</div>
<span>我是 span</span>
1.node.nextSibling
返回当前元素的下一个兄弟节点,找不到则返回null。同样获取的也是包含所有类型的节点(元素节点、属性节点、文本节点)
2.node.previousSibling
返回当前元素的上一个兄弟节点,找不到则返回null。也是包含所有类型
console.log(div.nextSibling); //得到#text文本节点,因为div的下一个是一个换行符
console.log(div.previousSibling); //#text,div的上一个节点还是换行^ ^
那我如果只要下一个或上一个元素节点呢
3.node.nextElementSibling
返回当前元素的下一个兄弟元素节点,找不到返回null
4.node.previousElementSibling
返回当前元素的上一个兄弟元素节点,找不到返回null
console.log(div.nextElementSibling); // <span>我是 span</span>
console.log(span.previousElementSibling); // <div>我是 div</div>
这两个方法同样还是有兼容性问题,ie9以上才ok
如何解决兼容性问题?
自己封装一个函数
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) { //while中的条件是:如果往下找还有则继续
if (el.nodeType == 1) {
return el;
}
}
return null;
}
创建、添加节点
见如下结构
<!--需求:本来ul里面是没有li的,我想要动态创建一个li放进去 -->
<ul>
<li>123</li>
</ul>
1.创建节点 document.createElement('tagName')
这个方法创建由tagName指定的HTML元素,因为这些元素原先不存在,是根据我们的需求动态生成的,所以也称为动态创建元素节点。
比如
var li = document.createElement('li');
var li2 = document.createElement('li');
可是,只创建节点还是不够的,我们还要把创建好的节点添加进去!
2.添加节点
(1) node.appendChild(child) node是父级,child是子级
这个方法将一个节点添加到指定父节点node的子节点列表末尾。类似css中after伪元素
var ul = document.querySelector('ul');
ul.append(li); //把刚刚动态生成的li添加到父级ul中的子列表末尾
(2) node.insertBeforce(child,指定元素)
node.insertBeforce()方法将一个节点添加到父节点 指定子节点的前面
ul.insertBefore(li2, ul.children[0]); //在ul的第一个元素节点前添加li2
总结:页面想要添加一个新的元素,1.创建元素 2.添加元素
删除节点
<button>删除</button>
<ul>
<li>张三</li>
<li>李四</li>
<li>王五</li>
</ul>
1.删除节点 node.removeChild(child)
该方法从DOM中删除一个子节点,返回删除的节点
// 获取元素
var ul = document.querySelector('ul');
// 点击按钮依次删除
var btn = document.querySelector('button');
btn.onclick = function () {
if (ul.children.length == 0) {
this.disabled = true;
alert('列表中已经没有元素');
} else {
ul.removeChild(ul.children[0]);
}
}
克隆节点
node.cloneNode()
返回调用该方法的节点的一个副本
如果括号中参数为空或者false,则是浅拷贝,只复制节点本身,不复制节点中的子节点。如果是true则是深拷贝
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
var ul = document.querySelector('ul');
var li = ul.children[0].cloneNode(true); //改为false则是浅拷贝
ul.appendChild(li);
这是克隆后的结果图(深拷贝)
浅拷贝
以上,如有错误请多多指教~