目录
学习目的
获取元素常用的两种方式:
1. 利用 DOM 提供的方法获取元素
- document.getElementById()
- document.getElementByTagName()
- document.querySelector()
- document.querySelectorAll() 等
- 逻辑性不强、繁琐
2. 利用节点层级关系获取元素
- 利用父子兄节点关系获取元素
- 逻辑性强,但兼容性较差
以上两种方式都可以获取元素节点,后面都会使用,但是节点操作更简单
节点概述
- 网页中的所有内容都是节点(标签、属性、文本、注释等)
- 在 DOM 中,节点使用 node 来表示
- HTML DOM 树中的所有节点均可通过 JavaScript 进行访问
- 所有 HTML 元素(节点)均可被修改,也可以被创建和删除
- 文档节点、元素节点、文本节点、属性节点
- 文字、空格、换行属于文本节点
一般情况下,节点至少拥有以下基本属性: nodeType(节点类型)、nodeName(节点名称)和 nodeValue(节点值)
- 元素节点 nodeType 为 1(标签)
- 属性节点 nodeType 为 2
- 文本节点 nodeType 为 3(文本节点包括文字、空格、换行等)
- 实际开发中,节点操作主要操作的是元素节点
节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系
父节点
- node.parentNode
<body>
<div class="box">
<span class="erweima"></span>
</div>
<script>
// 父节点 parentNode
var erweima = document.querySelector('.erweima');
// var box = document.querySelector('.box');
// 得到的是离该元素最近的父节点,如果找不到返回 null
// 其他的祖先节点无法通过 parentNode 得到
console.log(erweima.parentNode); // 得到父节点
</script>
</body>
子节点
获取所有子节点
- parentNode.childNodes (标准)
- parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合
注意
- 返回值里面包含所有的子节点,包含元素节点、文本节点等
- 如果只想要获取里面的元素节点,需要 for 循环
- 因此,一般不提倡使用 childNodes
<script>
// 通过 DOM 提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelector('li');
// 通过子节点获取
// childNodes 获取所有的子节点;包含元素节点、文本节点等等
console.log(ul.childNodes); // NodeList(5) [text, li, text, li, text]
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 只返回子元素节点
var ol = document.querySelector('ol');
for (var i = 0; i < ol.childNodes.length; i++) {
if (ol.childNodes[i].nodeType == 1) {
console.log(ol.childNodes[i]);
}
}
</script>
获取所有子元素节点
- parentNode.children (非标准)
- parentNode.children 是一个只读属性,返回所有的子元素节点
- 只返回子元素节点,其余节点不返回
- 虽然 children 是一个非标准,但是所有浏览器都支持,可以放心使用
<script>
// children 获取所有子元素节点
// 实际开发中常用方法
console.log(ol.children); // HTMLCollection(2) [li, li]
</script>
获取第一个和最后一个子元素节点
获取第一个子节点
- parentNode.firstChild
- 返回第一个子节点,找不到返回 null
- 包含所有节点
获取最后一个子节点
- parentNode.lastChild
- 返回最后一个子节点,找不到返回 null
- 包含所有节点
获取第一个子元素节点
- parentNode.firstElementChild
- 返回第一个子元素节点
- 只返回子元素节点
- IE9 以上才支持
获取最后一个子元素节点
- parentNode.lastElementChild
- 返回最后一个子元素节点
- 只返回子元素节点
- IE9 以上才支持
实际开发中:element.children[i] 返回指定某一个子元素节点
- firstChild 和 lastChild 包含其他节点,操作不方便
- firstElementChild 和 lastElementChild 又存在兼容性问题
- 由于 element.children 的返回值为一个伪数组,所以在实际开发中可以使用 children[i] 返回指定的某一个子元素节点
<body>
<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// firstChild 第一个子节点
console.log(ol.firstChild); // #text
// lastChild 最后一个子节点
console.log(ol.lastChild); // #text
// firstElementChild 第一个子元素节点 IE9 以上支持
console.log(ol.firstElementChild); // 1
// lastElementChild 最后一个子元素节点 IE9 以上支持
console.log(ol.lastElementChild); // 5
// 实际开发中,借助伪数组,使用 ol.children[i] 获取指定的子元素节点
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
下拉菜单
案例分析
- 导航栏里面的 li 都要有鼠标经过效果,所以需要循环注册鼠标事件
- 核心原理:当鼠标经过 li 里面的 第二个子元素 ul 显示;鼠标离开,ul 隐藏
<script>
// 获取元素
var nav = document.querySelector('.nav');
var lis = nav.children; // 得到四个 li
// 循环注册事件
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.children[1].style.display = 'block';
}
}
for (var i = 0; i < lis.length; i++) {
lis[i].onmouseout = function() {
this.children[1].style.display = 'none';
}
}
</script>
兄弟节点
得到上(下)一个兄弟节点
- node.nextSibling
- nextSibling 返回当前元素的下一个兄弟节点,找不到则返回 null
- 包含所有节点
- node.previousSibling
- previousSibling 返回当前元素的上一个兄弟节点,找不到则返回 null
- 包含所有节点
得到上(下)一个兄弟元素节点
- node.nextElementSibling
- nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回 null
- node.previousElementSibling
- previousElementSibling 返回当前元素的上一个兄弟元素节点,找不到返回 null
- 兼容性问题,IE9 以上才支持
<body>
<div>欲穷千里目</div>
<span>更上一层楼</span>
<script>
var div = document.querySelector('div');
// 得到上(下)一个兄弟节点
console.log(div.nextSibling); // #text
console.log(div.previousSibling); // #text
// 得到上(下)一个兄弟元素节点
console.log(div.nextElementSibling); // <span>
console.log(div.previousElementSibling); // null
</script>
</body>
解决兼容性问题
- 自己封装一个兼容性的函数
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if(el.nodeType === 1) {
return el;
}
}
return null;
}
一般情况下,父节点和子节点就够用了
创建和添加节点
创建节点
- document.createElement('tagName')
- 创建由 tagName 指定的、原先不存在的 HTML 元素
- 根据需求动态生成指定的 HTML 元素,因此也可以称之为动态创建元素节点
添加节点
添加节点到指定父节点的子节点列表末尾
- node.appendChild(child);node 父级,child 子级
- 将一个节点添加到指定父节点的子节点列表末尾
- 类似于 CSS 里面的 after 伪元素
- 类似于数组中的 push() 方法
添加节点到父节点的指定子节点前面
- node.insertBefore(child, node.children[i])
- 将一个节点添加到父节点的指定子节点的前面
- 类似于 CSS 中的 before 伪元素
<body>
<ul>
<li></li>
</ul>
<script>
// 1.创建元素节点
var li = document.createElement('li');
// 2.添加元素节点 添加子节点到父节点的子节点列表末尾
// node.appendChild(child)
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3.添加节点到父节点的指定子节点的前面
// node.insertBefore(child, node.children[i])
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
</script>
</body>
简单发布留言案例
- 核心思路:点击按钮之后,动态添加一个 li,并将其动态添加到 ul 里面
- 创建 li 的同时,把文本域里面的值通过 li.innerHTML 赋值给 li
- 想要把新的留言放在最后显示用 appendChild,想要放在前面显示就用 insertBefore
<body>
<textarea name="" id=""></textarea>
<button>发布</button>
<ul></ul>
<script>
var button = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
button.onclick = function() {
if (text.value == '') {
alert('您并未输入内容,请输入您的评论:');
return false;
} else {
// 创建元素
var li = document.createElement('li');
// 先有 li,才能赋值
li.innerHTML = text.value; // 将文本域的值赋给 li
// 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
}
}
</script>
</body>
删除节点
- node.removeChild(node.children[i])
- 从 DOM 中删除一个子节点,返回删除的节点
<body>
<button>删除</button>
<ul>
<li>光头强</li>
<li>黑心虎</li>
<li>灰太狼</li>
</ul>
<script>
// 获取元素
var ul = document.querySelector('ul');
var button = document.querySelector('button');
// 删除节点
// ul.removeChild(ul.children[1]);
// 点击按钮,依次删除子节点
button.onclick = function() {
if (ul.children.length == 0) {
this.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>
var button = document.querySelector('button');
var text = document.querySelector('textarea');
var ul = document.querySelector('ul');
button.onclick = function() {
if (text.value == '') {
alert('您并未输入内容,请输入您的评论:');
return false;
} else {
// 创建元素
var li = document.createElement('li');
// 先有 li,才能赋值
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>"; // 将文本域的值赋给 li
// 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
// 删除元素 删除的是当前链接的 li ,链接 a 的父级节点
var as = document.querySelectorAll('a');
for (var i = 0; i < as.length; i++) {
as[i].onclick = function() {
// node.removeChild(child); 删除的是当前连接 a 所在的 li this.parentNode;
ul.removeChild(this.parentNode);
}
}
}
}
</script>
</body>
复制节点(克隆节点)
- node.cloneNode()
- 返回调用该方法的节点的一个副本
- 也称为克隆节点、拷贝节点
注意:
- 如果括号中参数为空或者为 false,则是浅拷贝
- 即只克隆复制节点本身,不克隆里面的子节点
- 也可以说只复制标签,不复制里面的内容
- 如果参数为 true,深拷贝
- 克隆复制节点本身的同时也克隆里面的子节点
- 复制标签同时复制里面的内容
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 克隆节点:node.cloneNode()
// 当括号中的参数为空或 false 时,浅拷贝
var lili = ul.children[0].cloneNode();
// 添加克隆节点
ul.appendChild(lili);
// 克隆节点:node.cloneNode(true) 深拷贝
var lili1 = ul.children[1].cloneNode(true);
ul.appendChild(lili1);
</script>
</body>
综合案例
动态生成表单
案例分析
- 因为里面的学生数据都是动态的,需要 JS 动态生成。这里我们自己定义好数据。数据采取对象形式存储
- 所有的数据都放在 tbody 中的行里
- 因为行很多,需要循环创建多行(对应人数)
<body>
<table cellspacing="0">
<thead>
<tr>
<th>姓名</th>
<th>职业</th>
<th>专精值</th>
<th>武器</th>
<th>操作</th>
</tr>
</thead>
<tbody></tbody>
</table>
<script>
// 先准备数据
var datas = [{
name: '刘备',
job: '君王',
speciality: 90,
weapon: '雌雄双股剑'
}, {
name: '诸葛亮',
job: '谋士',
speciality: 100,
weapon: '孔明扇'
}, {
name: '关羽',
job: '统帅',
speciality: 96,
weapon: '青龙偃月刀'
}, {
name: '张飞',
job: '武将',
speciality: 95,
weapon: '丈八蛇矛'
}, {
name: '浦沅',
job: '大匠',
speciality: 90,
weapon: '浦沅刀'
}];
// 往 tbody 中创建行
var tbody = document.querySelector('tbody');
for (var i = 0; i < datas.length; i++) { // 外面的 for 循环创建行
var tr = document.createElement('tr');
tbody.appendChild(tr);
// 行内创建单元格 td, 单元格的数量取决于每个对象里面的属性个数
// 通过 for 循环创建对象
for (var key in datas[i]) { // 里面的 for 循环创建单元格
var td = document.createElement('td');
// 在创建单元格的同时将对象里面的值给 td
td.innerHTML = datas[i][key];
tr.appendChild(td);
}
// 创建删除单元格
var td = document.createElement('td');
td.innerHTML = '<a href="javascript:;">删除 </a>';
tr.appendChild(td);
}
// 删除操作
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>
三种动态创建元素方式
- document.write()
- element.innnerHTML
- document.createElement()
区别
- document.write() 直接将内容写入页面的内容流,
- 如果页面文档流执行完毕,再调用此语句将会导致页面全部重绘
- 将会重新创建一个 html 文档,里面只包含 document.write() 写入的内容
<body>
<button>点击</button>
<p>金沙水拍云崖暖</p>
<script>
// 了解
// window.onload = function() {
// document.write('<div>乌蒙蓬勃走泥丸</div>')
// }
// 三种创建元素方式的区别
// 1.document.write()
document.write('<div>五岭逶迤腾细浪</div>');
var button = document.querySelector('button');
button.onclick = function() {
// 文档流执行完毕后,再执行此语句,将会导致文档重绘
document.write('<span>大渡桥横铁索寒</span>');
}
// 2.innerHTML 创建元素
// 3.document.createElement() 创建元素
</script>
</body>
innerHTML 和 createElement
效率对比
- 使用 innerHTML 创建元素时,如果采取拼接字符串的方式,每次创建都需要开辟新的空间,因此花费时间较长,效率较低 3000 毫秒
- 使用 innerHTML 创建元素时,如果采取数组的方式,效率最高,大约是 5~7 毫秒
- 与 innerHTML 相比,createElement() 效率更高,大约是 17 毫秒
区别
- innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
- innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
- createElement() 创建多个元素效率稍微低一些,但是结构更清晰
总结:再不同的浏览器下,innerHTML 效率要比 createElement 高
<body>
<div class="inner"></div>
<div class="create"></div>
<script>
// 1.innerHTML 创建元素
// 使用字符串拼接
// 每次创建都需要开辟新的空间,效率最低,3000 单位时间
var inner = document.querySelector('.inner');
for (var i = 0; i < 100; i++) {
inner.innerHTML += '<a href="#">百度</a>';
}
// 使用数组形式拼接
// 效率最高 5~7 单位时间
var arr = [];
for (var i = 0; i < 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join(''); // 将数组转换为字符串
// 2.document.createElement() 创建元素
// 效率较高,17 单位时间
var create = document.querySelector('.create');
for (var i = 0; i < 100; i++) {
var a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>