5 节点操作
5.1 为什么学习节点操作
5.2 节点概述
一般情况下,节点至少拥有节点类型(nodeType)、节点名称(nodeName)、节点值(nodeValue)这三个基本属性
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="box">
<span class="erweima">x</span>
</div>
<script>
var box = document.querySelector('.box');
console.dir(box); //可以通过这个方式查看节点的属性
</script>
</body>
查看部分属性:
- 元素节点的nodeType为1(常用)
- 属性节点的nodeType为2
- 文本节点的nodeType为3(包含文字空格换行等)
5.3 节点层级
5.3.1 父级节点
node.parentNode//得到的是距离node最近的父级元素节点,如果找不到其父节点就返回为null
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="demo">
<div class="box">
<span class="erweima">x</span>
</div>
</div>
<script>
//获取box和erweima这两个盒子
//过去的做法
// var erweima = document.querySelector('.erwima');
// var box = document.querySelector('.box');
//1. 父节点parentNode 现在的做法
var erweima = document.querySelector('.erweima');
console.log(erweima.parentNode); //得到的是距离元素erweima最近的父级节点box,如果找不到其父节点就返回为null
</script>
</body>
5.3.2 子节点
- 标准:
parentNode.childNodes//返回包含指定节点parentNode的子节的的集合(包括元素节点、文本节点等)
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<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>
<div class="demo">
<div class="box">
<span class="erweima"></span>
</div>
</div>
<script>
//获取ul里面的li
//过去的方法
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
//现在的方法
//1. 子节点 返回包含指定节点的子节的的集合,该集合为即时更新的集合
console.log(ul.childNodes); //NodeList(9) [text, li, text, li, text, li, text, li, text] 返回值里包含了所有的节点 包括元素节点、文本节点等 中间有5个换行,所以有5个文本节点text
console.log(ul.childNodes[0].nodeType); //3 文本节点的nodeType的属性是3
console.log(ul.childNodes[1].nodeType); //1 元素节点的nodeType的属性是1
//为了获得其中的元素节点需要专门处理(所以一般不提倡用childNodes)
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
console.log(ul.childNodes[i]);
}
}
</script>
</body>
- 非标准:
parentNode.children//可以得到所有的子元素节点 是一个只读属性 返回一个伪数组
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<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>
<div class="demo">
<div class="box">
<span class="erweima"></span>
</div>
</div>
<script>
//获取ul里面的li
//过去的方法
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
//现在的方法
//1. 子节点 返回包含指定节点的字节的的集合,该集合为即时更新的集合
console.log(ul.childNodes); //NodeList(9) [text, li, text, li, text, li, text, li, text] 返回值里包含了所有的节点包括元素节点、文本节点等 中间有5个换行,所以有5个文本节点text
console.log(ul.childNodes[0].nodeType); //3 文本节点的nodeType的属性是3
console.log(ul.childNodes[1].nodeType); //1 元素节点的nodeType的属性是1
//为了获得其中的元素节点需要专门处理(所以一般不提倡用childNodes)
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
console.log(ul.childNodes[i]);
}
}
//2. 获取所有的子元素节点
console.log(ul.children); //HTMLCollection(4) [li, li, li, li]
</script>
</body>
子节点的相关操作:获取第一个子元素和最后一个子元素
parentNode.firstChild //返回第一个子节点,找不到则返回null。同样也是包含所有的节点
parentNode.lastChild //返回最后一个子节点,找不到则返回null。同样也是包含所有的节点
parentNode.firstElementChild //返回第一个子节点,找不到则返回null。只包含元素节点
parentNode.lastElementChild //返回最后一个子节点,找不到则返回null。只包含元素节点
注意:后两个方法有兼容性问题,ie9以上才支持
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<script>
var ol = document.querySelector('ol');
//1. 不仅仅包含元素节点
console.log(ol.firstChild); //#text 文本节点
console.log(ol.lastChild); //#text 文本节点
//2. 兼容性问题
console.log(ol.firstElementChild); //<li>我是li1</li>
console.log(ol.lastElementChild); //<li>我是li4</li>
//3. 实际开发 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]); //<li>我是li1</li>
console.log(ol.children[ol.children.length - 1]); //<li>我是li4</li>
</script>
</body>
案例:下拉菜单
- 导航栏里面的li都要有鼠标经过的效果,所以需要循环注册鼠标事件
- 核心原理:当鼠标经过li里面的第二个孩子(ul)显示,当鼠标离开,则ul隐藏。
<body>
<ul class="nav">
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
<script>
//1. 获取元素
var nav = document.querySelector('.nav');
var lis = nav.querySelectorAll('li'); //得到4个小li
//2. 循环注册事件
for (var i = 0; i < lis.length - 1; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
</body>
样式内容省略
5.3.3 兄弟节点
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
//1. node.nextSibling 返回当前元素的下一个兄弟节点,找不到则返回null。包含所有节点
console.log(div.nextSibling); //#text
//2. node.previousSibling 返回当前元素的上一个兄弟节点,找不到则返回null。包含所有节点
console.log(div.previousSibling); //#text
//3. node.nextElementSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。
console.log(div.nextElementSibling); //<span>我是span</span>
//4. node.previousElementSibling 返回当前元素的上一个兄弟元素节点,找不到则返回null。
console.log(div.previousElementSibling); //null 因为div上面没有兄弟了
//后两个方法依然有兼容性问题,ie9以上才支持
//解决方法:自己封装一个函数(了解)
function getnextElementSibling(element){
var el = element;
while(el = el.nextSibling) {
if(el.nodeType == 1) {
return el;
}
}
return null;
}
</script>
</body>
5.4 创建节点 和添加节点---->document.creatElement()、node.appendChild()、node.insertBefore()
- 创建
document.creatElement('tagName')//创建由tagName指定的HTML元素。因为这些元素原先不存在,是我们根据需求动态生成的,所以我们也称为动态创建元素节点。
- 添加
node.appendChild(child)//将一个节点添加到指定父节点的子节点列表的末尾。类似于css元素里的after伪元素。
node.insertBefore(child,指定元素)//将一个节点添加到父节点指定子节点前面。类似于css里面的before伪元素。
<body>
<ul>
<li>123</li>
</ul>
<script>
//在ul里动态地再创建一个li
//1. 创建元素节点
var li = document.createElement('li');
//2. 添加节点 node.appendChild(child) node是父级 child是子级 在后面追加元素
var ul = document.querySelector('ul');
ul.appendChild(li);
//1. 创建元素节点
var lili = document.createElement('li');
//3. 添加节点node.insertBefore(child,指定元素)
ul.insertBefore(lili, ul.children[0]);
</script>
</body>
案例:简单版发布留言案例
- 思路: 当点击按钮之后就动态创建一个li,之后再把li创建到ul之中就可以了
- 在创建li的同时,把文本域里面的值通过li.innerHTML赋值给li
- 如果想要新的留言后面显示就用appendChild,如果想要前面显示就用insertBefore
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
//1. 获取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
//2. 注册事件
btn.onclick = function() {
if (text.value == '') {
alert("您没有输入内容");
return false; //终止操作
} else {
//(1)创建元素
var li = document.createElement('li');
//给li赋值
li.innerHTML = text.value;
//(2)添加元素
// ul.appendChild(li);新留言在最前面
ul.insertBefore(li, ul.children[0]);
}
}
</script>
</body>
样式省略
5.5 删除节点---->node.removeChild()
- 语法:
node.removeChild(child)//从DOM中删除一个子节点,返回删除的节点
- 举例:
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
//点击一次按钮删除一个li,到最后没有li的时候,按钮将禁用
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
btn.onclick = function() {
if (ul.children.length == 0) {
btn.disabled = true;
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
删除留言案例
- 当我们把文本域里面的值赋值给li的时候,多添加一个删除连接
- 需要把所有的链接获取过来,当点击当前的链接的时候,删除当前连接所在的li
- 阻止连接跳转添加javascript:void(0)或者javascript:;
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul>
</ul>
<script>
//1. 获取元素
var btn = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
//2. 注册事件
btn.onclick = function() {
if (text.value == '') {
alert("您没有输入内容");
return false; //终止操作
} else {
//(1)创建元素
var li = document.createElement('li');
//给li赋值
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
//(2)添加元素
// ul.appendChild(li);新留言在最后
ul.insertBefore(li, ul.children[0]);
//(3) 删除元素 删除的是当前链接的li 是a的父亲
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
ul.removeChild(this.parentNode);//删除的是当前的a所在的li this.parentNode
}
}
}
}
</script>
</body>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 100px;
}
textarea {
200px;
height: 100px;
border: 1px solid pink;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
300px;
padding: 5px;
background-color: rgb(245, 209, 243);
color: red;
font-size: 14px;
margin: 15px 0;
}
li a {
float: right;
text-decoration: none;
}
</style>
5.5 复制节点(克隆节点)
- 语法
node.cloneNode()//返回该方法节点的一个副本
-
注意:
(1)如果括号参数为空或者为false,则是浅拷贝,即只是克隆节点本身,不克隆节点里的子节点
(1)如果括号参数为true,则是深拷贝,不只克隆节点本身,还克隆节点里的子节点 -
举例
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
//克隆节点
var liliq = ul.children[0].cloneNode(); //浅拷贝
var lilis = ul.children[0].cloneNode(true); //深拷贝
//将克隆的节点放在ul中的最末的位置
ul.appendChild(liliq);
ul.appendChild(lilis);
</script>
</body>
案例:动态生成表格
表格里的数据用对象的形式存储,因为里面既有姓名又有科目成绩
表格分为thead和tbody,其中thead部分的内容不变,tbody的内容是js动态生成的,在js动态生成之前只有表头部分:如下:
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
//先准备好学生数据,用数组datas存放对象
var datas = [{
name: '卫英络',
subject: 'javascript',
score: 100
}, {
name: '红利',
subject: 'javascript',
score: 98
}, {
name: '富恒',
subject: 'javascript',
score: 85
}, {
name: '明玉',
subject: 'javascript',
score: 60
}];
//往tbody里面创建行:有几个人(数组长度)就创建几行
var tbody = document.querySelector('tbody');
//1. 创建tr行
for (var i = 0; i < datas.length; i++) { //外层for循环管行
var tr = document.createElement('tr');
tbody.appendChild(tr);
//2. 在行里面创建单元格 与数据(姓名、科目、成绩)相关的单元格
for (var k in datas[i]) { //内存for循环管每行里面创建多少个单元格,每个对象里有多少个属性就创建几个单元格
var td = document.createElement('td');
//将对象datas[i]里的属性值datas[i][k]给td
td.innerHTML = datas[i][k];
tr.appendChild(td);
}
//3. 创建删除单元格(最后一列)
var td = document.createElement('td');
td.innerHTML = "<a href='javascript:;'>删除</a>";
tr.appendChild(td);
}
//4. 删除操作开始
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
//点击a,删除a所在的行tr(a 的父亲是td, td的父亲是tr) node.removeChild(child)
tbody.removeChild(this.parentNode.parentNode);
}
}
//说明:
// for(var k in obj) {//obj是某对象
// k得到的是属性名
// obj[k]得到的是属性值
// }
</script>
</body>
<style>
table {
500px;
margin: 100px auto;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
</style>
5.6三种动态创建元素的区别---->document.write()、element.inneHTML、document.creatElement()
- document.write()
- element.inneHTML
- document.creatElement()
区别:
(1)document.write()是直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘。原先页面里面的内容就没有了
<body>
<button>点击</button>
<p>abc</p>
<script>
//三种创建元素方式的区别
//1. document.write() 创建元素
var btn = document.querySelector('button');
btn.onclick = function() { //按钮点击是在文档流执行完毕
document.write('<div>123</div>'); //按钮点击之后,文档里面只有内容123
}
</script>
</body>
(2)innerHTML是将内容写入某个DOM节点,不会导致页面全部重绘
(3)innerHTML创建多个元素效率更高(不要拼接字符串,采取数组式拼接),但结构稍微复杂
(4)creatElement()创建多个元素效率稍微低一点,但是结构更清晰
总结:在不同浏览器下,创建多个标签innerHTML效率比creatElement高;创建单个标签没什么大的区别
<body>
<button>点击</button>
<p>abc</p>
<div class="inner"></div>
<div class="create"></div>
<script>
//三种创建元素方式的区别
//2. innerHTML 创建元素 创建100个a
var inner = document.querySelector('.inner');
for (var i = 0; i <= 100; i++) {
inner.innerHTML += '<a href="#">百度</a>'////以拼接字符串的方式 用时很长
}
//3. document.createElement()创建元素 创建100个a
var creat = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>
<body>
<button>点击</button>
<p>abc</p>
<div class="inner"></div>
<div class="create"></div>
<script>
//三种创建元素方式的区别
//2. innerHTML 创建元素 创建100个a
var inner = document.querySelector('.inner');
// for (var i = 0; i <= 100; i++) {
// inner.innerHTML += '<a href="#">百度</a>'////以拼接字符串的方式 用时很长
// }
var arr = [];
for (var i = 0; i <= 100; i++) { //用数组形式拼接 效率高
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
//3. document.createElement()创建元素 创建100个a
var creat = document.querySelector('.create');
for (var i = 0; i <= 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>