5 节点操作
获取元素的方式:
比较发现,用节点层级关系来获取元素更简单(DOM方法相当于绝对路径,节点方法相当于相对路径)。
5.1 节点概述
概念: 网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM中,节点使用node来表示。
基本组成: nodeType(节点类型) 、nodeName(节点名称)、nodeValue(节点值)
- 元素节点nodeType为1
- 属性节点nodeType为2
- 文本节点nodeType为3(文本节点包含文字、空格、换行等)
节点操作(主要为元素节点
)的目的: 获取元素
5.2 节点层级
利用DOM树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
5.2.1 父子节点
1.父节点
node.parentNode
2.子节点
node.childNodes
(标准),返回子节点(元素节点、文本节点…)集合node.children
(非标准),返回子元素节点集合(各浏览器都支持,更常用)node.firstChild
,返回第一个子节点(所有类型),找不到返回nullnode.lastChild
,返回最后一个子节点(所有类型)node.firstElementChild
,返回第一个子元素节点,IE9以上才支持node.lastElementChild
,返回最后一个子元素节点,IE9以上才支持node.children[0]
,返回第一个子元素节点,没有兼容性问题node.children[ol.children.length - 1])
,返回ol
最后一个子元素节点,没有兼容性问题
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- 节点的优点 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// DOM 提供的方法(API)获取 '父节点'
var erweima = document.querySelector('.erweima');
var box = document.querySelector('.box'); // 以前获得.box方法
// 父节点 node.parentNode
console.log(erweima.parentNode); // 现在获得.box方法为erweima.parentNode,返回<div class="box">...</div>,得到的是离元素最近的父级节点(亲爸爸)box,得不到demo,如果找不到父节点就返回为 null
// DOM 提供的方法(API)获取 '子节点'
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. node.childNodes 获得所有的子节点,包含:元素节点、文本节点...
console.log(ul.childNodes); // NodeList(9)
console.log(ul.childNodes[0].nodeType); // 3(文本节点)
console.log(ul.childNodes[1].nodeType); // 1(元素节点)
// 如何只获取元素节点?
var ul = document.queryselector ('ul');
for(var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
console.log (ul.childNodes[i]); // ul.childNodes[i]是元素节点
}
} // 这种方法太麻烦,因此用方法2
// 2. node.children 获取所有的子元素节点,更常用
console.log(ul.children); // HTMLCollection(4)
var ol = document.querySelector('ol');
// 3.4. node.firstChild
console.log(ol.firstChild); // #text
console.log(ol.lastChild); // #text
// 5.6. node.firstElementChild,IE9以上才支持
console.log(ol.firstElementChild); // <li>我是li1</li>
console.log(ol.lastElementChild); // <li>我是li4</li>
// 7.8. node.children[0]实际开发的写法,没有兼容性问题
console.log(ol.children[0]); // <li>我是li1</li>
console.log(ol.children[ol.children.length - 1]); // <li>我是li4</li>
</script>
</body>
</html>
案例:下拉菜单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style-type: none;
}
a {
text-decoration: none;
font-size: 14px;
}
.nav {
margin: 100px;
}
.nav>li {
position: relative;
float: left;
width: 80px;
height: 41px;
text-align: center;
}
.nav li a {
display: block;
width: 100%;
height: 100%;
line-height: 41px;
color: #333;
}
.nav>li>a:hover {
background-color: #eee;
}
.nav ul {
display: none;
position: absolute;
top: 41px;
left: 0;
width: 100%;
border-left: 1px solid #FECC5B;
border-right: 1px solid #FECC5B;
}
.nav ul li {
border-bottom: 1px solid #FECC5B;
}
.nav ul li a:hover {
background-color: #FFF5DA;
}
</style>
</head>
<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>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
<li>
<a href="#">微博</a>
<ul>
<li>
<a href="">私信</a>
</li>
<li>
<a href="">评论</a>
</li>
<li>
<a href="">@我</a>
</li>
</ul>
</li>
</ul>
<script>
// 1. 获取元素
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到4个小li
// 2.循环注册事件
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
</body>
</html>
鼠标指向菜单后出现下拉菜单,离开后自动隐藏:
5.2.2 兄弟节点
node.nextSibling
,返回元素下一个兄弟节点(所有类型),找不到返回nullnode.previousSibling
,返回元素上一个兄弟节点(所有类型),找不到返回nullnode.nextElementSibling
,返回元素下一个兄弟元素节点,找不到返回null,IE9以上才支持node.previousElementSibling
,返回元素上一个兄弟元素节点,找不到返回null,IE9以上才支持
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.2. nextSibling 下一个兄弟节点,包含:元素节点、文本节点...
console.log(div.nextSibling); // #text
console.log(div.previousSibling); // #text
// 3.4. nextElementSibling 下一个兄弟元素节点,IE9以上才支持
console.log(div.nextElementSibling); // <span>我是span</span>
console.log(div.previousElementSibling); // null
// 有兼容性问题怎么办?封装一个兼容性函数。
function getNextElementsibling (element) {
var el = element;
while (el = el.nextsibling) {
if (el.nodeType === 1) { // 是元素节点就返回
return el;
}
}
return null;
}
</script>
</body>
</html>
5.3 添加元素
在页面中添加元素的步骤:
- 创建节点
document.createElement('tagName')
,由tagName指定的HTML元素创建。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点
。
- 添加节点
node.appendChild(child);
,将一个节点添加到指定父节点的子节点列表末尾
,类似于CSS里面的after伪元素。node.insertBefore(child,指定元素);
将一个节点添加到指定父节点的指定子节点前面
,类似于CSS里面的before伪元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li>123</li>
</ul>
<script>
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点node.appendChild(child),类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li); // 给ul父节点内的末尾加上小li
// 3. 添加节点node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]); // ul.children[0]为第一个孩子,在它前面插入lili
</script>
</body>
</html>
案例:简单版-发布留言
需求:输入留言内容,点击发布后内容会出现在下面。
思路:点击按钮后动态创建一个li,添加到ul里面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 100px;
}
textarea {
width: 200px;
height: 100px;
border: 1px solid pink;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
width: 300px;
padding: 5px;
background-color: rgb(245, 209, 243);
color: red;
font-size: 14px;
margin: 15px 0;
}
</style>
</head>
<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 {
// console.log(text.value); 留言的内容
// (1) 创建元素
var li = document.createElement('li'); // 先有li
li.innerHTML = text.value; // 再进行赋值
// (2) 添加元素
// ul.appendChild(li); 新写的留言在最后面显示
ul.insertBefore(li, ul.children[0]); // 新写的留言在最前面显示
}
}
</script>
</body>
</html>
5.4 删除节点
node.removeChild(child)
,删除子节点并返回删除的节点
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button>删除</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光头强</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 删除元素 node.removeChild(child)
// ul.removeChild(ul.children[0]); 熊大被删掉
// 3. 点击按钮依次删除里面第一个孩子
btn.onclick = function() {
if (ul.children.length == 0) {
this.disabled = true; // 如果没有元素,按钮就被禁用
} else {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
</html>
案例:删除留言
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
padding: 100px;
}
textarea {
width: 200px;
height: 100px;
border: 1px solid pink;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
width: 300px;
padding: 5px;
background-color: rgb(245, 209, 243);
color: red;
font-size: 14px;
margin: 15px 0;
}
li a {
float: right;
}
</style>
</head>
<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.innerHTML = text.value + "<a href='javascript:;'>删除</a>"; // 使用javascript:阻止链接跳转,效果比#好
// (2) 添加元素
ul.insertBefore(li, ul.children[0]);
// (3) 删除元素:删除的是当前链接的li(它的父亲)
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
ul.removeChild(this.parentNode); // node.removeChild(child); 删除的是当前a所在的li,this.parentNode;
}
}
}
}
</script>
</body>
</html>
5.5 复制节点
node.cloneNode ();
,括号为空或者里面是false,为浅拷贝
,只复制标签不复制里面的内容node.cloneNode(true);
, 括号为true,为深拷贝
,复制标签里面的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 浅拷贝
// 2. node.cloneNode(true); 深拷贝
var lili = ul.children[0].cloneNode(true);
ul.appendChild(lili);
</script>
</body>
</html>
浅拷贝效果: 深拷贝效果:
案例:动态生成表格
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
table {
width: 500px;
margin: 100px auto;
border-collapse: collapse;
text-align: center;
}
td,
th {
border: 1px solid #333;
}
thead tr {
height: 40px;
background-color: #ccc;
}
</style>
</head>
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>科目</th>
<th>成绩</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!-- 放准备好的学生数据,一个学生放一行 -->
</tbody>
</table>
<script>
// 1.先准备好学生的数据(实际应用:JS动态生成,从数据库中取数据;此处:模拟数据)
var datas = [{ // 一个数组中放了多个对象
name: '魏璎珞',
subject: 'JavaScript',
score: 100
}, {
name: '弘历',
subject: 'JavaScript',
score: 98
}, {
name: '傅恒',
subject: 'JavaScript',
score: 99
}, {
name: '明玉',
subject: 'JavaScript',
score: 88
}];
// 2. 往tbody里面创建行:有几个人(看数组的长度)就创建几行
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) { // 外面的for循环指行 tr
var tr = document.createElement('tr'); // 创建tr行
tbody.appendChild(tr);
// 行里面创建3个单元格td for循环遍历对象 datas[i]
for (var k in datas[i]) { // 里面的for循环指列 td
var td = document.createElement('td'); // 创建单元格
td.innerHTML = datas[i][k]; // 把对象里面的属性值datas[i][k] 给td
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所在的行(链接的爸爸的爸爸) node.removeChild(child)
tbody.removeChild(this.parentNode.parentNode)
}
}
</script>
</body>
</html>
5.6 三种动态创建元素的区别
document.write()
,直接将内容写入页面的内容流,但是文档流执行完毕,则它会导致页面全部重绘element.innerHTML
,不会导致页面全部重绘,创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂document.createElement()
,创建多个元素效率稍低一点点,但是结构更清晰
总结:不同浏览器下,innerHTML效率要比creatElement高
6 DOM小结
-
创建
-
document.write
-
innerHTML
-
createElement
-
-
增加
-
appendChild
-
insertBefore
-
-
删除
- removeChild
-
修改(元素属性、元素内容、表单的值…)
-
元素属性:src、href、title等
-
普通元素内容:innerHTML、innerText
-
表单元素:value、type、disabled等
-
元素样式:style、className
-
-
查询(DOM元素)
-
DOM提供的API方法:getElementByld、getElementsByTagName 古老用法不太推荐
-
H5提供的新方法:querySelector、querySelectorAll 提倡
-
利用节点操作获取元素:父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡
-
-
属性操作(主要针对自定义属性)
-
setAttribute:设置dom的属性值
-
getAttribute:得到dom的属性值
-
removeAttribute:移除属性
-
-
事件操作(给元素注册事件,采取
事件源.事件类型=事件处理程序
)