操作元素
JavaScript的 DOM 操作可以改变网页内容、结构和样式,我们可以利用 DOM 操作元素来改变元素里面的内容、属性等。
1. 改变元素内容
html
element.innerText
从起始位置到终止位置的内容,但它去除html标签,同时空格和换行也会去掉
html
element.innerHTML
起始位置到终止位置的全部内容,包括html标签,同时保留空格和换行
设置内容的时候,当设置不含标签的内容的时候应该使用 innerText,效率更高。
- innerText 的兼容性处理
innerText 和 textContent 都可以获取文本内容,但是有浏览器兼容问题,下面学习一下兼容性处理的思想。
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="box">
hello
</div>
<script>
var box = document.getElementById('box');
console.log(getInnerText(box));
// 处理innerText的兼容性问题
function getInnerText(element) {
// 判断当前浏览器 是否支持元素的innerText属性
if (typeof element.innerText === 'string') { // 当属性存在的时候返回的是该属性的类型
return element.innerText;
} else { // 当属性不存在的时候返回的是undefined
// 如果不支持innerText属性,使用element.textContent获取内容
return element.textContent;
}
}
</script>
</body>
</html>
2. 常用元素的属性操作
- innerText、innerHTML 改变元素内容
- src、href
- id、alt、title
示例代码:
js
<body>
<button id="ldh">刘德华</button>
<button id="zxy">张学友</button> <br>
<img src="images/ldh.jpg" alt="" title="刘德华">
<a href="跳转目标" target="目标窗口的弹出方式">文本或图像</a>
<script>
// 修改元素属性 src
// 1. 获取元素
var ldh = document.getElementById('ldh');
var zxy = document.getElementById('zxy');
var img = document.querySelector('img');
// 2. 注册事件 处理程序
zxy.onclick = function() {
img.src = 'images/zxy.jpg';
img.title = '张学友思密达';
}
ldh.onclick = function() {
img.src = 'images/ldh.jpg';
img.title = '刘德华';
}
</script>
</body>
3. 表单元素的属性操作
利用DOM可以操作如下表单元素的属性:
js
type 可以获取input标签的类型(输入框或复选框等)
value 用于大部分表单元素的内容获取(option除外)
checked 复选框选中属性 (布尔类型)
selected 下拉菜单选中属性 (布尔类型)
disabled 禁用属性 (布尔类型)
示例代码:
js
<body>
<button>按钮</button>
<input type="text" value="输入内容">
<script>
// 1. 获取元素
var btn = document.querySelector('button');
var input = document.querySelector('input');
// 2. 注册事件 处理程序
btn.onclick = function() {
// 表单里面的值 文字内容是通过 value 来修改的
input.value = '被点击了';
// 如果想要某个表单被禁用
// btn.disabled = true;
this.disabled = true;
// this 指向的是事件函数的调用者 btn
}
</script>
</body>
① 案例:给文本框赋值,获取文本框的值
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input type="text"><br>
<input id="btn" type="button" value="获取文本框的值">
<script>
// 1 当页面加载完毕,给所有的文本框赋值
var inputs = document.getElementsByTagName('input');
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
// 根据type属性 判断是否是文本框
if (input.type === 'text') {
input.value = i; // 给文本框赋值
}
}
// 2 当点击按钮的时候,获取所有文本框的值,并使用 | 分割输出
// 0|1|2
var btn = document.getElementById('btn');
btn.onclick = function () {
var array = [];
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
// 判断是否是文本框
if (input.type === 'text') {
//大量字符串的拼接会有性能问题,所以我们先放到一个数组里面,再用数组拼接
array.push(input.value);
}
}
console.log(array.join('|')); // 打印:0|1|2|3|4|5|6|7|8
}
</script>
</body>
</html>
② 案例:点击下拉框左边的设置,随机选中一项
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="button" value="设置" id='btnSet'>
<select id="selCities">
<option value="1">北京</option>
<option value="2">上海</option>
<option value="3">杭州</option>
<option value="4">郑州</option>
<option value="5">武汉</option>
</select>
<script>
// 1 给按钮注册事件
var btnSet = document.getElementById('btnSet');
btnSet.onclick = function () {
// 2 获取下拉框中的所有option
var selCities = document.getElementById('selCities');
var options = selCities.getElementsByTagName('option');
// 3 随机生成索引
// Math.random() -> [0, 1)
// Math.random() * 5 -> [0, 5)
var randomIndex = parseInt(Math.random() * options.length);
// 4 根据索引获取option,并让option选中
var option = options[randomIndex];
option.selected = true;
}
</script>
</body>
</html>
4. 通过操作style属性改变样式
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
js
元素对象.style.样式属性 = 值;
注意:
- JS 里面的样式采用驼峰命名法,比如:fontSize、backgroundColor
- JS 修改style样式操作,产生的行内样式,CSS权重比较高
示例代码:
js
<body>
<div></div>
<script>
// 1. 获取元素
var div = document.querySelector('div');
// 2. 注册事件 处理程序
div.onclick = function() {
// div.style里面的属性 采取驼峰命名法
this.style.backgroundColor = 'purple';
// 通过样式属性设置宽高、位置的属性类型是字符串,需要加上px
this.style.width = '250px';
}
</script>
</body>
① 案例:隐藏显示二维码
js
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
......
.hide {
display: none;
}
.show {
display: block;
}
</style>
</head>
<body>
<div class="nodeSmall" id="node_small">
<div class="erweima hide" id="er">
<img src="images/456.png" alt=""/>
</div>
</div>
<script src="common.js"></script>
<script>
// 根据id获取元素 封装成函数
function my$(id) {
return document.getElementById(id);
}
// 当鼠标移入 onmouseover
// 当鼠标移出 onmouseout
var nodeSmall = my$('node_small');
nodeSmall.onmouseover = function () {
// my$('er').className = 'erweima show';
my$('er').className = my$('er').className.replace('hide', 'show');
}
nodeSmall.onmouseout = function () {
// my$('er').className = 'erweima hide';
my$('er').className = my$('er').className.replace('show', 'hide');
}
</script>
</body>
</html>
② 案例:显示隐藏文本框内容
当鼠标点击文本框时,里面的默认文字隐藏,当鼠标离开文本框时,里面的文字显示
案例分析:
- 首先表单需要两个新事件,获得焦点onfocus,失去焦点onblur
- 如果获得焦点,判断表单里面内容是否为默认文字,如果是,则清空表单内容
- 如果失去焦点,判断表单内容是否为空,如果为空,则表单内容改为默认文字
js
// 1.获取元素
var text = document.querySelector('input');
// 2.注册事件 获得焦点事件 onfocus
text.onfocus = function() {
// console.log('得到了焦点');
if (this.value === '手机') {
this.value = '';
}
// 获得焦点需要把文本框里面的文字颜色变黑
this.style.color = '#333';
}
// 3. 注册事件 失去焦点事件 onblur
text.onblur = function() {
// console.log('失去了焦点');
if (this.value === '') {
this.value = '手机';
}
// 失去焦点需要把文本框里面的文字颜色变浅色
this.style.color = '#999';
}
5. 通过操作className属性改变样式
js
元素对象.className = 值;
Tip:因为class是关键字,所以使用className。
注意:
- 如果样式修改比较多,可以采用操作类名方式更改元素样式
- class因为是个保留字,因此使用className来操作元素类名属性
- className会直接更改元素的类名,会覆盖原先的类名
示例代码:
html
<body>
<div class="first">文本</div>
<script>
// 1. 使用 element.style 获得修改元素样式 如果样式比较少 或者 功能简单的情况下使用
var test = document.querySelector('div');
test.onclick = function() {
// this.style.backgroundColor = 'purple';
// this.style.color = '#fff';
// this.style.fontSize = '25px';
// this.style.marginTop = '100px';
// 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
// 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
// this.className = 'change';
this.className = 'first change';
}
</script>
</body>
案例:密码框格式提示错误信息
用户如果离开密码框,里面输入的文字个数不是6~16,显示红色小图标,输入正确显示绿色小图标。
案例分析:
- 首先判断的事件是表单失去焦点onblur
- 如果输入正确,则提示正确信息:绿色小图标
- 如果输入不是6~16位,则提示错误信息:红色小图标
- 因为里面变化样式较多,所以我们采用className修改样式
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>
div {
width: 600px;
margin: 100px auto;
}
.message {
display: inline-block;
font-size: 12px;
color: #999;
background: url(images/mess.png) no-repeat left center;
padding-left: 20px;
}
.wrong {
color: red;
background-image: url(images/wrong.png);
}
.right {
color: green;
background-image: url(images/right.png);
}
</style>
</head>
<body>
<div class="register">
<input type="password" class="ipt">
<p class="message">请输入6~16位密码</p>
</div>
<script>
// 首先判断的事件是表单失去焦点 onblur
// 如果输入正确则提示正确的信息颜色为绿色小图标变化
// 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
// 因为里面变化样式较多,我们采取className修改样式
// 1.获取元素
var ipt = document.querySelector('.ipt');
var message = document.querySelector('.message');
//2. 注册事件 失去焦点
ipt.onblur = function() {
// 根据表单里面值的长度 ipt.value.length
if (this.value.length < 6 || this.value.length > 16) {
// console.log('错误');
message.className = 'message wrong';
message.innerHTML = '您输入的位数不对要求6~16位';
} else {
message.className = 'message right';
message.innerHTML = '您输入的正确';
}
}
</script>
</body>
</html>
6. 排他思想(首先干掉其他人,再设置自己)
点击如下某个按钮,这个按钮变粉色,其他按钮恢复原色。
如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:
- 所有元素全部清除样式(干掉其他人)
- 给当前元素设置样式 (留下我自己)
- 注意顺序不能颠倒,首先干掉其他人,再设置自己
js
<button>按钮1</button>
<button>按钮2</button>
<button>按钮3</button>
<button>按钮4</button>
<button>按钮5</button>
<script>
// 1. 获取所有按钮元素
var btns = document.getElementsByTagName('button');
// btns得到的是伪数组 里面的每一个元素 btns[i]
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
// (1) 我们先把所有的按钮背景颜色去掉 干掉所有人
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
// (2) 然后才让当前的元素背景颜色为pink 留下我自己
this.style.backgroundColor = 'pink';
}
}
</script>
案例:全选反选
js
<script>
var j_tb = document.getElementById('j_tb');
var inputs = j_tb.getElementsByTagName('input');
// 1 全选
// 1.1 获取父checkbox,注册点击事件
var j_cbAll = document.getElementById('j_cbAll');
j_cbAll.onclick = function () {
// 1.2 找到所有子的checkbox,让这些checkbox的状态跟父checkbox保持一致
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.type === 'checkbox') {
// 让子的checkbox的选中状态,和父checkbox的选中状态一致
input.checked = this.checked;
}
}
}
// 2 当点击子的checkbox,如果所有的子的checkbox都被选中了,让父的checkbox也选中
// 如果有一个子的checkbox没有被选中,父的checkbox也不被选中
// 此处的循环,是遍历所有子的checkbox,注册点击事件
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
// 判断是否是checkbox
if (input.type !== 'checkbox') {
// 结束当前循环,继续下一次循环
continue;
}
// 给子的checkbox注册点击事件
input.onclick = function () {
checkAllCheckBox();
}
}
// 判断父的checkbox的状态 封装成函数
function checkAllCheckBox() {
// 假设所有的子的checkbox都被选中了
var isAllChecked = true;
// 判断是否所有的子的checkbox都被选中了
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
if (input.type !== 'checkbox') {
continue;
}
// 判断当前的所有checkbox是否都被选中
if (!input.checked) {
isAllChecked = false;
}
}
// 设置父的checkbox的状态
j_cbAll.checked = isAllChecked;
}
// 3 反选
// 3.1 给反选按钮注册点击事件
var btn = document.getElementById('btn');
btn.onclick = function () {
// 3.2 找到所有的子的checkbox,让其反选
for (var i = 0; i < inputs.length; i++) {
var input = inputs[i];
// 判断是否是checkbox
if (input.type !== 'checkbox') {
continue;
}
// 让子的checkbox反选
input.checked = !input.checked;
// 设置父的checkbox的状态
checkAllCheckBox();
}
}
</script>
7. 自定义属性操作
① 获取属性值 getAttribute()
js
element.属性; 获取内置属性值(元素本身自带的属性)
element.getAttribute('属性'); 主要获取自定义的属性(标准),我们程序员自定义的属性
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<img src="haha.png" id="hahaha" class="image" index="1">
<script>
var img = document.querySelector('img');
// 1. 获取元素的属性值
// (1) element.属性
console.log(img.src); // file:///Users/zjnx1111/Desktop/haha.png
console.log(img.id); // hahaha
console.log(img.class); // undefined
console.log(img.index); // undefined
// (2) element.getAttribute('属性') get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
console.log(img.getAttribute('src')); // haha.png
console.log(img.id); // hahaha
console.log(img.getAttribute('class')); // image
console.log(img.getAttribute('index')); // 1
</script>
</body>
</html>
② 设置属性值 setAttribute(,)
js
element.属性 = '值'; 设置内置属性值
element.setAttribute('属性', '值'); 主要设置自定义的属性(标准)
③ 移除属性 removeAttribute()
js
element.removeAttribute('属性');
案例:tab栏
当鼠标点击上面相应的选项卡(tab),下面内容跟随变化
案例分析:
- Tab栏切换有两个大的模块
- 上面的模块选项卡,点击某一个,当前这个的底色是红色,其他不变(排他思想),修改类名的方式
- 下面的模块内容,会跟随上面的选项卡变化,所以下面模块变化写到点击事件里面
- 规律:下面的模块显示内容和上面的选项卡一一对应,相匹配
- 核心思路:给上面的tab_list里面的所有小li添加自定义属性,属性值从0开始
- 当我们点击tab_list里面的某个小li,让tab_con里面对应序列号的内容显示,其余隐藏(排他思想)
js
<script>
// 获取元素
var tab_list = document.querySelector('.tab_list');
var lis = tab_list.querySelectorAll('li');
var items = document.querySelectorAll('.item');
// for循环,给选项卡绑定点击事件
for (var i = 0; i < lis.length; i++) {
// 开始给5个小li 设置索引号
lis[i].setAttribute('index', i);
lis[i].onclick = function() {
// 1. 上的模块选项卡,当前这一个底色会是红色,其余不变(排他思想)
// 干掉所有人 其余的li清除 class 这个类
for (var i = 0; i < lis.length; i++) {
lis[i].className = '';
}
// 留下我自己
this.className = 'current';
// 2. 下面的显示内容模块
var index = this.getAttribute('index');
console.log(index);
// 干掉所有人 让其余的item 这些div 隐藏
for (var i = 0; i < items.length; i++) {
items[i].style.display = 'none';
}
// 留下我自己 让对应的item 显示出来
items[index].style.display = 'block';
}
}
</script>
④ H5自定义属性 data-和dataset
有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性,所以H5给我们新增了自定义属性,H5规定,自定义属性data-开头作为属性值,并且赋值。dataset是h5新增的方法,得到的是一个集合,里面存放了所有以data开头的自定义属性。
- 设置H5自定义属性
H5规定自定义属性的属性名以data-
开头,比如:
js
<div data-index='1'> </div>
或者使用JS设置:
element.setAttribute('data-index', '2')
- 获取H5自定义属性
js
兼容性获取 element.getAttribute('data-index');
H5新增element.dataset.index 或者 element.dataset['index'] ie11才支持
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div = document.querySelector('div');
console.log(div.getTime); // undefined
console.log(div.getAttribute('getTime')); // 20
div.setAttribute('data-time', 30); // 新增自定义属性
console.log(div.getAttribute('data-time')); // 30
console.log(div.getAttribute('data-index')); // 2
console.log(div.getAttribute('data-list-name')); // andy
// dataset是h5新增的方法,得到的是一个集合,里面存放了所有以data开头的自定义属性
console.log(div.dataset); // DOMStringMap {index: "2", listName: "andy", time: "20"}
console.log(div.dataset.index); // 2
console.log(div.dataset['index']); // 2
// 如果自定义属性里面有多个-链接的单词,我们获取的时候采取驼峰命名法
console.log(div.dataset.listName); // andy
console.log(div.dataset['listName']); // andy
</script>
</body>
</html>
8. 节点操作
① 节点概述
网页中的所有内容都是节点(元素、属性、文本、注释等),在DOM 中,节点使用 node 来表示。
HTML DOM 树中的所有节点均可通过 JavaScript 进行访问,所有 HTML 元素均可被修改,也可以创建或删除。
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType为1
- 属性节点 nodeType为2
- 文本节点 nodeType为3(文本节点包含文字、空格、换行等)
我们实际开发中,主要操作的是元素节点。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="box" value="我是div">hello</div>
<script>
var div = document.querySelector('div');
console.log(div.nodeType); // 1
console.log(div.nodeName); // DIV
// 元素内的文本是文本节点,所以我们通过childNodes[0]获取这个文本节点,然后通过nodeValue获取文本节点的值
console.log(div.childNodes[0].nodeValue); // hello
</script>
</body>
</html>
② 节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄弟关系。
③ 父节点 node.parentNode
js
node.parentNode
- parentNode属性可返回某节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回null
js
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父节点 parentNode
var erweima = document.querySelector('.erweima');
// var box = document.querySelector('.box');
// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 null
console.log(erweima.parentNode);
</script>
④ 子节点 parentNode.children
所有子节点:
js
parentNode.childNodes (标准)
parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为及时更新的集合。
注意:返回值里面包含了所有的子节点,包括元素节点、文本节点等。如果只想要获得里面的元素节点,则需要专门处理,所以我们一般不提倡使用childNodes。
子元素节点:
js
parentNode.children (非标准)
parentNode.children 是一个只读属性,返回所有的子元素节点。它返回子元素节点,其余节点不返回(这个是我们重点掌握的)。虽然children是一个非标准,但却得到了各个浏览器的支持,因此我们可以放心使用。
js
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<script>
// DOM 提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children);
</script>
第1个子节点:
js
parentNode.firstChild
firstChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。
最后1个子节点:
js
parentNode.lastChild
lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。
第1个子元素节点:
js
parentNode.firstElementChild
firstElementChild 返回第一个子元素节点,找不到则返回null。
最后1个子元素节点:
js
parentNode.lastElementChild
lastElementChild 返回最后一个子元素节点,找不到则返回null。
注意:这两个方法有兼容性问题,IE9以上才支持。
兼容性处理:
实际开发中,firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点呢?
解决方案:都不用,我们使用children,如下:
js
如果想要第一个子元素节点,可以使用:parentNode.children[0]
如果想要最后一个子元素节点,可以使用:parentNode.children[parentNode.children.length - 1]
js
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一个子节点 不管是文本节点还是元素节点
console.log(ol.firstChild);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]);
console.log(ol.children[ol.children.length - 1]);
</script>
案例:新浪下拉菜单
案例分析:
- 导航栏里面的li都要有鼠标经过效果,所以需要循环注册鼠标事件
- 核心原理:当鼠标经过li里面的第二个孩子ul显示,当鼠标离开,则ul隐藏
js
<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>
......
//nav里面有四个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() {
// 3.显示ul
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
// 3.隐藏ul
this.children[1].style.display = 'none';
}
}
</script>
</body>
⑤ 兄弟节点
下一个兄弟节点:
js
node.nextSibling
返回当前元素的下一个兄弟节点,找不到则返回null。
上一个兄弟节点:
js
node.previousSibling
返回当前元素上一个兄弟节点,找不到则返回null。
下一个兄弟元素节点(有兼容性问题):
js
node.nextElementSibling
返回当前元素下一个兄弟元素节点,找不到则返回null。
上一个兄弟元素节点(有兼容性问题):
js
node.previousElementSibling
返回当前元素上一个兄弟元素节点,找不到则返回null。
js
<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>
注意:nextElementSibling和previousElementSibling有兼容性问题,IE9以上才支持。
问:如何解决兼容性问题? 答:自己封装一个兼容性的函数
获取下一个兄弟元素节点,兼容性处理:
js
// 获取下一个兄弟元素节点
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
⑥ 子节点和兄弟节点总结
js
//节点包括:元素节点、属性节点、文本节点、注释节点
var box = document.getElementById('box');
console.log(box.parentNode); //父节点 只有一个
console.log(box.hasChildNodes()); //是否有子节点
console.log(box.childNodes); //子节点 有很多个
console.log(box.firstChild); //第一个子节点
console.log(box.lastChild); //最后一个子节点
console.log(box.nextSibling); //下一个兄弟节点
console.log(box.previousSibling); //上一个兄弟节点
js
//元素节点
console.log(box.children); //子元素集合
console.log(box.firstElementChild); //第一个子元素,有兼容性问题,从IE9以后支持
console.log(box.lastElementChild); //最后一个子元素,有兼容性问题,从IE9以后支持
console.log(box.nextElementSibling); //下一个兄弟元素,有兼容性问题,从IE9以后支持
console.log(box.previousElementSibling); //上一个兄弟元素,有兼容性问题,从IE9以后支持
⑦ 创建节点 createElement()
js
document.createElement('tagName')
document.createElement() 方法创建由tagName指定的HTML元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。
js
// 在内存中创建一个DOM对象
var div = document.createElement('div'); //创建div
// 设置对象的属性
div.innerText = 'hello';
div.style.color = 'red';
document.body.appendChild(div); //追加div
⑧ 添加节点 node.appendChild(child)
js
node.appendChild(child)
node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于css里面的after伪元素。
js
node.insertBefore(child, 指定元素)
node.insertBefore( , ) 方法将一个节点添加到父节点的指定子节点前面。类似于css里面的before伪元素。
js
<ul>
<li>123</li>
</ul>
<script>
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3. 添加节点 node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
</script>
案例:简单版发布留言
案例分析:
- 核心思路:点击按钮之后,就动态创建一个li,添加到ul里面
- 创建li的同时,把文本域里面的值通过li.innerHTML赋值给li
- 如果想到新的留言后面显示,就用appendChild,如果想要前面显示就用insertBefore
js
<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(child)
js
node.removeChild(child)
node.removeChild() 方法从 node节点中删除一个子节点,返回删除的节点。
js
<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>
案例:删除留言
案例分析:
- 当我们把文本域里面的值赋值给li的时候,多添加一个删除的链接
- 需要把所有的链接获取过来,当我们点击当前的链接的时候,删除当前链接所在的li
- 阻止链接跳转需要添加javascript:void(0); 或者 javascript:;
js
<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);
// 添加到最前面,用insertBefore
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>
🔟 复制节点 node.cloneNode(false/true);
js
node.cloneNode()
node.cloneNode() 方法返回调用该方法的节点的一个副本。也称为克隆节点/拷贝节点
注意:
- 如果括号参数为空或者为false,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为true,则是深度拷贝,会复制节点本身以及里面所有的子节点。
js
<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>
案例:动态创建表格
js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
#box table {
border-collapse: collapse;
}
</style>
</head>
<body>
<div id="box"></div>
<script src="common.js"></script>
<script>
// var s = {name: 'zs', subject: '语文', score: 90};
// 模拟数据
var datas = [
{name: 'zs', subject: '语文', score: 90},
{name: 'ls', subject: '数学', score: 80},
{name: 'ww', subject: '英语', score: 99},
{name: 'zl', subject: '英语', score: 100},
{name: 'xs', subject: '英语', score: 60},
{name: 'dc', subject: '英语', score: 70}
];
// 表头数据
var headDatas = ['姓名', '科目', '成绩', '操作'];
// 1 创建table 元素
var table = document.createElement('table');
my$('box').appendChild(table);
table.border = '1px';
table.width = '400px';
// 2 创建表头
var thead = document.createElement('thead');
table.appendChild(thead);
var tr = document.createElement('tr');
thead.appendChild(tr);
tr.style.height = '40px';
tr.style.backgroundColor = 'lightgray';
// 遍历头部数据,创建th
for (var i = 0; i < headDatas.length; i++) {
var th = document.createElement('th');
tr.appendChild(th);
// th.innerText
setInnerText(th, headDatas[i]);
}
// 3 创建数据行
var tbody = document.createElement('tbody');
table.appendChild(tbody);
tbody.style.textAlign = 'center';
for (var i = 0; i < datas.length; i++) {
// 一个学生的成绩 {name: 'zl', subject: '英语', score: 100},
var data = datas[i];
tr = document.createElement('tr');
tbody.appendChild(tr);
// 遍历对象
for (var key in data) {
var td = document.createElement('td');
tr.appendChild(td);
setInnerText(td, data[key]);
}
// 生成删除对应的列
td = document.createElement('td');
tr.appendChild(td);
// 删除的超链接
var link = document.createElement('a');
td.appendChild(link);
link.href = 'javascript:void(0)';
setInnerText(link, '删除');
// 点击删除的事件
link.onclick = linkDelete;
}
function linkDelete() {
// 获取要删除的行
var tr = this.parentNode.parentNode;
tbody.removeChild(tr);
return false;
}
</script>
</body>
</html>
案例:移动水果
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
select {
width:200px;
height: 200px;
background-color: #33cccc;
font-size: 20px;
}
</style>
</head>
<body>
<select id="all" multiple="multiple">
<option>苹果</option>
<option>橘子</option>
<option>梨</option>
<option>西瓜</option>
<option>水蜜桃</option>
</select>
<input type="button" value=">>" id="btn1">
<input type="button" value="<<" id="btn2">
<input type="button" value=">" id="btn3">
<input type="button" value="<" id="btn4">
<select id="select" multiple="multiple">
</select>
<script src="common.js"></script>
<script>
var all = my$('all');
var select = my$('select');
// 1 全部选择
my$('btn1').onclick = function () {
// 错误的 因为children中的项被移走之后,索引会重新排列
// for (var i = 0; i < all.children.length; i++) {
// var option = all.children[i];
// select.appendChild(option);
// }
//
// 倒序遍历,是都可以全部移过去,但是水果的顺序发生颠倒了
// for (var i = all.children.length - 1; i >= 0; i--) {
// var option = all.children[i];
// select.appendChild(option);
// }
//
// 先把children.length取出来,这样移动元素的时候就不会实时变化了
// 现在len的值始终是当前获取到的all.children.length 当前个数5
var len = all.children.length;
for (var i = 0; i < len; i++) {
//一直获取索引为0的元素
var option = all.children[0];
select.appendChild(option);
}
// 补充:这种方式更简单,但是有很多弊端
// select.innerHTML = all.innerHTML;
// all.innerHTML = '';
// 这两行代码都有问题:
// 第一行:使用这种方式移动子元素的话,如果子元素有事件,移动之后元素的事件丢失
// 第二行:当我们使用innerHTML=''清空子元素的时候,如果子元素有事件,此时会发生内存泄漏
}
// 3 移动选中的水果
my$('btn3').onclick = function () {
// 先找到所有选中的option,再把option放到数组中,再把数组中的option移动到第二个select中
// 这样就不用管元素索引变化的问题和元素顺序的问题
var array = []; // 存储选中的option
for (var i = 0; i < all.children.length; i++) {
var option = all.children[i];
if (option.selected) {
array.push(option);
// 去掉当前option的选中效果
option.selected = false;
}
}
// 把数组中的option移动到第二个select中
for (var i = 0; i < array.length; i++) {
var option = array[i];
select.appendChild(option);
}
}
</script>
</body>
</html>
(11) 创建元素的三种方式
js
document.write()
element.innerHTML
document.createElement()
区别:
- document.write 是直接将内容写入页面的内容流,如果文档流已经执行完毕了,则它会导致页面全部重绘
- innerHTML 是将内容写入某个DOM节点,不会导致页面全部重绘
- innerHTML 创建多个元素效率更高(不要拼接字符串,采用数组形式拼接),结构稍微复杂
- createElement() 创建多个元素效率稍微低一点,但是结构更清晰
总结:不同浏览器下,innerHTML 效率要比 createElement 高
js
<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>
(12) innerTHML和createElement()效率对比
innerHTML字符串拼接方式(效率低):
js
<script>
function fn() {
var d1 = +new Date();
var str = '';
for (var i = 0; i < 1000; i++) {
// innerHTML字符串拼接方式(效率低)
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方式(效率一般):
js
<script>
function fn() {
var d1 = +new Date();
for (var i = 0; i < 1000; i++) {
// createElement方式(效率一般)
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数组方式(效率高):
js
<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>');
}
// innerHTML数组方式(效率高)
document.body.innerHTML = array.join('');
var d2 = +new Date();
console.log(d2 - d1);
}
fn();
</script>