节点概述
网页中的所有内容都是节点(标签、属性、文本、注释等),在DOM 中,节点使用 node 来表示。HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素(节点)均可被修改,也可以创建或删除。
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
节点层级:利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
父节点
node.parentNode
- 返回某节点的父节点,最近的一个父节点
- 如果没有父节点,则返回null
子节点
parentNode.childNodes
- 返回指定节点的子节点的集合,会即时更新
- 会返回所有的节点(包含元素节点、文本节点等)
parentNode.children
- 只读属性
- 只返回元素节点
- 非标准,但是各个浏览器都支持
parent.firstChild

-
parent.lastChild
-
parent.firstElementChild
返回第一个子元素节点,找不到返回null
-
parent.lastElementChild
返回最后一个子元素节点,找不到返回null
- IE9以上支持
首末节点兼容方案
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?
兄弟节点
下一个兄弟节点
上一个兄弟节点
<div>我是div</div><span>我是span</span><script>var div = document.querySelector('div');// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等console.log(div.nextSibling);console.log(div.previousSibling);// 2. nextElementSibling 得到下一个兄弟元素节点console.log(div.nextElementSibling);console.log(div.previousElementSibling);</script>
下一个兄弟元素节点(有兼容性问题)
上一个兄弟元素节点(有兼容性问题)


function getNextElementSibling(element) {var el = element;while (el = el.nextSibling) {if (el.nodeType === 1) {return el;}}return null;}
创建结点
document.createElement()
- document.createElement(‘tagName’)
- 动态创建元素节点
node.cloneNode()

<ul><li>1111</li><li>2</li><li>3</li></ul><script>var ul = document.querySelector('ul');// 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容// 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容var lili = ul.children[0].cloneNode(true);ul.appendChild(lili);</script>
Demo:动态生成表格


<script>// 1.先去准备好学生的数据var datas = [{name: '魏璎珞',subject: 'JavaScript',score: 100}, {name: '弘历',subject: 'JavaScript',score: 98}, {name: '傅恒',subject: 'JavaScript',score: 99}, {name: '明玉',subject: 'JavaScript',score: 88}, {name: '大猪蹄子',subject: 'JavaScript',score: 0}];// 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行var tbody = document.querySelector('tbody');// 遍历数组for (var i = 0; i < datas.length; i++) {// 1. 创建 tr行var tr = document.createElement('tr');tbody.appendChild(tr);// 2. 行里面创建单元格td 单元格的数量取决于每个对象里面的属性个数// 使用for in遍历学生对象for (var k in datas[i]) {// 创建单元格var td = document.createElement('td');// 把对象里面的属性值 datas[i][k] 给 tdtd.innerHTML = datas[i][k];tr.appendChild(td);}// 3. 创建有删除2个字的单元格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>
创建元素的三种方式对比

<script>// 三种创建元素方式区别// 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘var btn = document.querySelector('button');btn.onclick = function() {document.write('<div>123</div>');}// 2. innerHTML 创建元素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() 创建元素var create = document.querySelector('.create');for (var i = 0; i <= 100; i++) {var a = document.createElement('a');create.appendChild(a);}</script>
innerTHML和createElement效率对比
innerHTML字符串拼接方式(效率低)
<script>function fn() {var d1 = +new Date();var str = '';for (var i = 0; i < 1000; i++) {document.body.innerHTML += '<div style="width:100px; height:2px; border:1px solid blue;"></div>';}var d2 = +new Date();console.log(d2 - d1);}fn();</script>
createElement方式(效率一般)
<script>function fn() {var d1 = +new Date();for (var i = 0; i < 1000; i++) {var div = document.createElement('div');div.style.width = '100px';div.style.height = '2px';div.style.border = '1px solid red';document.body.appendChild(div);}var d2 = +new Date();console.log(d2 - d1);}fn();</script>
innerHTML数组方式(效率高)
<script>function fn() {var d1 = +new Date();var array = [];for (var i = 0; i < 1000; i++) {array.push('<div style="width:100px; height:2px; border:1px solid blue;"></div>');}document.body.innerHTML = array.join('');var d2 = +new Date();console.log(d2 - d1);}fn();</script>
插入结点
node.appendChild()
Demo:简单版发布留言


<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>
删除节点
node.removeChild()
- node.removeChild() 方法从 node节点中删除一个子节点,返回删除的节点。
Demo:删除留言


<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 + "<a href='javascript:;'>删除</a>";// (2) 添加元素// ul.appendChild(li);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() {// 删除的是 li 当前a所在的li this.parentNode;ul.removeChild(this.parentNode);}}}}</script>

