一、DOM
1.1 什么是DOM
文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
1.2 DOM 树
- 文档:一个页面就是一个文档,DOM 中使用 document 表示
- 元素:页面中的所有标签都是元素,DOM 中使用 element 表示
- 节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM 中使用 node 表示
DOM 把以上内容都看做是对象
二、获取元素
2.1 如何获取页面元素
获取页面中的元素可以使用以下几种方式:
- 根据 ID 获取
- 根据标签名获取
- 通过 HTML5 新增的方法获取
- 特殊元素获取
2.2 根据 ID 获取
document.getElementById('id');
使用 console.dir() 可以打印我们获取的元素对象,更好的查看对象里面的属性和方法。
<div id="time">2019-9-9</div>
<script>
// 1. 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
// 2. get 获得 element 元素 by 通过 驼峰命名法
// 3. 参数 id是大小写敏感的字符串
// 4. 返回的是一个元素对象
var timer = document.getElementById('time');
console.log(timer); // <div id="time">2019-9-9</div>
console.log(typeof timer); // Object
// 5. console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
console.dir(timer);
</script>
2.3 根据标签名获取
document.getElementsByTagName('标签名');
注意:
因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。
得到元素对象是动态的
如果获取不到元素,则返回为空的伪数组(因为获取不到对象)
还可以获取某个元素(父元素)内部所有指定标签名的子元素.
element.getElementsByTagName('标签名');
注意:父元素必须是单个对象(必须指明是哪一个元素对象). 获取的时候不包括父元素自己。
<body>
<ul>
<li>第一个小li</li>
<li>第二个小li</li>
</ul>
<script>
// 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
var lis = document.getElementsByTagName('li');
// 2.我们想要依次打印里面的元素对象我们可以采取遍历的方式
for (let i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// [...lis] 将伪数组转化为数组
// [...lis].forEach(item =>{
// console.log(item)
// });
// 3. 如果页面中只有一个li 返回的还是伪数组的形式
// 4. 如果页面中没有这个元素 返回的是空的伪数组的形式
// 5. element.getElementsByTagName('标签名'); 父元素必须是指定的单个元素
// var ol = document.getElementsByTagName('ol'); // [ol]
// console.log(ol.getElementsByTagName('li')); // 错误
// console.log(ol[0].getElementsByTagName('li'));
var oli = document.getElementById('ol').getElementsByTagName('li');
console.log(oli);
</script>
</body>
2.4 通过 HTML5 新增的方法获取
document.getElementsByClassName(‘类名’);// 根据类名返回元素对象集合
document.querySelector('选择器'); // 根据指定选择器返回第一个元素对象
document.querySelectorAll('选择器'); // 根据指定选择器返回
注意:
querySelector 和 querySelectorAll 里面的选择器需要加符号,比如:document.querySelector(’#nav’);
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div id="nav">
<ul>
<li>首页</li>
<li>产品</li>
</ul>
</div>
<script>
// 1. getElementsByClassName 根据类名获得某些元素集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
// 2. querySelector 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box #nav
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelectorAll()返回指定选择器的所有元素对象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
</body>
2.5 获取特殊元素 (body,html)
// 获取body元素
doucumnet.body // 返回body元素对象
// 获取html元素
document.documentElement // 返回html元素对象
三、事件基础
3.1 事件概述
JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。
简单理解: 触发 --------- 响应机制。
网页中的每个元素都可以产生某些可以触发 JavaScript 的事件,例如,我们可以在用户点击某按钮时产生一个 事件,然后去执行某些操作。
3.2 事件三要素
- 事件源 (谁)
- 事件类型 (什么事件)
- 事件处理程序 (做啥)
<body>
<button id="btn">点击</button>
<script>
// 点击一个按钮,弹出对话框
// 1. 事件是有三部分组成 事件源 事件类型 事件处理程序 我们也称为事件三要素
//(1) 事件源 事件被触发的对象 谁 按钮
var btn = document.getElementById('btn');
//(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下
//(3) 事件处理程序 通过一个函数赋值的方式 完成
btn.onclick = function() {
alert('响应');
}
</script>
</body>
3.2.1 执行事件的步骤
- 获取事件源
- 注册事件(绑定事件)
- 添加事件处理程序(采取函数赋值形式)
3.3 常见的鼠标事件
四、操作元素
4.1 改变元素内容
从起始位置到终止位置的内容, 但它去除 html 标签, 同时空格和换行也会去掉
element.innerText
起始位置到终止位置的全部内容,包括 html 标签,同时保留空格和换行
element.innerHTML
4.1.1 innerHTML(重要) 和 innerText 的区别
<body>
<div></div>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML的区别
var div = document.querySelectorAll('div');
// 1. innerText 不识别html标签 非标准 去除空格和换行
div[0].innerText = '<strong>我是:</strong> innerText';
// 2. innerHTML 识别html标签 W3C标准 保留空格和换行的
div[1].innerHTML = '<strong>我是:</strong> innerHTML';
// 这两个属性是可读写的 可以获取元素里面的内容
var p = document.querySelector('p');
console.log('p.innerText' + p.innerText);
console.log('p.innerHTML' + p.innerHTML);
</script>
</body>
效果图:
4.1.2 案例-点击获取当前时间
<!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,
p {
width: 400px;
height: 30px;
line-height: 30px;
color: #fff;
background-color: red;
}
</style>
</head>
<body>
<button>显示当前系统时间</button>
<div>某个时间</div>
<p>1123</p>
<script>
// 当我们点击了按钮, div里面的文字会发生变化
// 1. 获取元素
var btn = document.querySelector('button');
var div = document.querySelector('div');
// 2.注册事件
btn.onclick = function() {
div.innerHTML = getDate();
}
function getDate() {
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let week = date.getDay();
let days = date.getDate();
let h = date.getHours();
let m = date.getMinutes();
let s = date.getSeconds();
let weeks = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
// 值小于10 补0
month = month < 10 ? '0' + month: month;
days = days < 10 ? '0' + days: days;
h = h < 10 ? '0' + h: h;
m = m < 10 ? '0' + m: m;
s = s < 10 ? '0' + s: s;
// 反引号 模板字符串
let today = `现在时间是: ${year}年 ${month}月 ${days}日 ${weeks[week]} ${h}:${m}:${s}`;
// let today = '现在时间是:' + year + '年' + month + '月' + days + '日 ' + weeks[week] + h+':'+m+':'+s;
return today;
}
// 我们元素可以不用添加事件
var p = document.querySelector('p');
p.innerHTML = getDate();
</script>
</body>
</html>
效果图
4.2 常用元素的属性操作
- innerText、innerHTML 改变元素内容
- src、href
- id、alt、title
4.2.1 案例-点击按钮切换图片
<style>
div {
width: 600px;
margin: 100px auto;
}
img {
width: 300px;
}
</style>
<body>
<div>
<button id="cat">小猫</button>
<button id="dog">小狗</button> <br>
<img src="images/cat.jpg" alt="" title="小猫">
</div>
<script>
// 修改元素属性 src
// 1. 获取元素
var cat = document.getElementById('cat');
var dog = document.getElementById('dog');
var img = document.querySelector('img');
// 2. 注册事件 处理程序
dog.onclick = function() {
img.src = 'images/dog.jpg';
img.title = '小狗';
}
cat.onclick = function() {
img.src = 'images/cat.jpg';
img.title = '小猫';
}
</script>
</body>
效果图
4.2.2 案例-分时显示不同图片
<body>
<img src="images/s.gif" alt="">
<div>上午好</div>
<script>
// 根据系统不同时间来判断,所以需要用到日期内置对象
// 利用多分支语句来设置不同的图片
// 需要一个图片,并且根据时间修改图片,就需要用到操作元素src属性
// 需要一个div元素,显示不同问候语,修改元素内容即可
// 1.获取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
// 2. 得到当前的小时数
var date = new Date();
var h = date.getHours();
// 3. 判断小时数改变图片和文字信息
if (h < 12) {
img.src = 'images/s.gif';
div.innerHTML = '上午好,记得写代码';
} else if (h < 18) {
img.src = 'images/x.gif';
div.innerHTML = '下午好,好好写代码';
} else {
img.src = 'images/w.gif';
div.innerHTML = '晚上好,好好看代码';
}
</script>
</body>
4.3 表单元素的属性操作
type、value、checked、selected、disabled
<body>
<button>按钮</button>
<input type="text" value="输入内容">
<script>
// 1. 获取元素
var btn = document.querySelector('button');
var input = document.querySelector('input');
// 2. 注册事件 处理程序
btn.onclick = function() {
// input.innerHTML = '点击了'; 这个是 普通盒子 比如 div 标签里面的内容
// 表单里面的值 文字内容是通过 value 来修改的
input.value = '被点击了';
// 如果想要某个表单被禁用 不能再点击 disabled 我们想要这个按钮 button禁用
// btn.disabled = true;
// this.disabled = true;
// this 指向的是事件函数的调用者 btn
}
</script>
</body>
4.3.1 案例-仿京东显示密码
<!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>
.box {
position: relative;
width: 400px;
border-bottom: 1px solid #ccc;
margin: 100px auto;
}
.box input {
width: 370px;
height: 30px;
border: 0;
outline: none;
}
.box img {
position: absolute;
top: 2px;
right: 2px;
width: 24px;
}
</style>
</head>
<body>
<div class="box">
<label for="">
<img src="images/close.png" alt="" id="eye">
</label>
<input type="password" name="" id="pwd">
</div>
<script>
// 1. 获取元素
var eye = document.getElementById('eye');
var pwd = document.getElementById('pwd');
// 2. 注册事件 处理程序
var flag = 0;
eye.onclick = function() {
// 点击一次之后, flag 一定要变化
if (flag == 0) {
pwd.type = 'text';
eye.src = 'images/open.png';
flag = 1; // 赋值操作
} else {
pwd.type = 'password';
eye.src = 'images/close.png';
flag = 0;
}
}
</script>
</body>
</html>
效果图
4.4 样式属性操作
我们可以通过 JS 修改元素的大小、颜色、位置等样式
element.style 行内样式操作
element.className 类名样式操作
注意:
1. JS 里面的样式采取驼峰命名法 比如 fontSize、backgroundColo
2. JS 修改 style 样式操作,产生的是行内样式,CSS 权重比较高
3. 如果样式修改较多,可以采取操作类名方式更改元素样式。
4. class因为是个保留字,因此使用className来操作元素类名属性
5. className 会直接更改元素的类名,会覆盖原先的类名。
<body>
<div>点我</div>
<script>
// 1. 获取元素
var div = document.querySelector('div');
// 2. 注册事件 处理程序
div.onclick = function() {
// div.style里面的属性 采取驼峰命名法
this.style.fontSize = '24px';
this.style.color = 'red';
this.innerHTML = '我变了';
}
</script>
</body>
通过className更改元素样式
<style>
div {
width: 100px;
height: 100px;
background-color: pink;
}
.change {
background-color: purple;
color: #fff;
font-size: 25px;
margin-top: 100px;
}
</style>
<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';
// 让我们当前元素的类名改为了 change
// 2. 我们可以通过 修改元素的className更改元素的样式 适合于样式较多或者功能复杂的情况
// 3. 如果想要保留原先的类名,我们可以这么做 多类名选择器
// this.className = 'change';
this.className = 'first change';
}
</script>
</body>
4.4.1 案例-点击关闭淘宝二维码
<!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>
.box {
position: relative;
width: 74px;
height: 88px;
border: 1px solid #ccc;
margin: 100px auto;
font-size: 12px;
text-align: center;
color: #f40;
/* display: block; */
}
.box img {
width: 60px;
margin-top: 5px;
}
.close-btn {
position: absolute;
top: -1px;
left: -16px;
width: 14px;
height: 14px;
border: 1px solid #ccc;
line-height: 14px;
font-family: Arial, Helvetica, sans-serif;
cursor: pointer;
}
</style>
</head>
<body>
<div class="box">
淘宝二维码
<img src="images/tao.png" alt="">
<i class="close-btn">×</i>
</div>
<script>
// 1. 获取元素
var btn = document.querySelector('.close-btn');
var box = document.querySelector('.box');
// 2.注册事件 程序处理
btn.onclick = function() {
box.style.display = 'none';
}
</script>
</body>
</html>
4.4.2 案例-循环精灵图
精灵图:https://img.alicdn.com/tfs/TB1eiXTXlTH8KJjy0FiXXcRsXXa-24-595.png
<!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;
}
.box {
width: 250px;
margin: 100px auto;
}
.box li {
float: left;
width: 24px;
height: 24px;
background-color: pink;
margin: 15px;
background: url(images/sprite.png) no-repeat;
}
</style>
</head>
<body>
<div class="box">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<script>
// 首先精灵图图片排列有规律的
// 核心思路: 利用for循环 修改精灵图片的 背景位置 background-position
// 剩下的就是考验你的数学功底了
让循环里面的 i 索引号 * 44 就是每个图片的y坐标
// 1. 获取元素 所有的小li
var lis = document.querySelectorAll('li');
// for (var i = 0; i < lis.length; i++) {
// // 让索引号 乘以 44 就是每个li 的背景y坐标 index就是我们的y坐标
// let index = -i * 44 + 'px';
// lis[i].style.backgroundPosition = '0 ' + index;
// }
[...lis].forEach((value,index) => {
let i = -index * 44 + 'px';
value.style.backgroundPosition = `0 ${i}`;
})
</script>
</body>
</html>
效果图
4.4.3 案例-显示隐藏文本框内容
<!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>
input {
color: #999;
}
</style>
</head>
<body>
<input type="text" value="手机">
<script>
// 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';
}
</script>
</body>
</html>
效果图
4.4.4 案例-密码框格式提示信息
<!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>
效果图
4.4.5 案例-排他思想
<body>
<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 (let i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
// (1) 我们先把所有的按钮背景颜色去掉 干掉所有人
for (let i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
// (2) 然后才让当前的元素背景颜色为red 留下我自己
this.style.backgroundColor = 'red';
}
}
//2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想
</script>
</body>
效果图
4.4.6 案例-百度换肤
<style>
* {
margin: 0;
padding: 0;
}
body {
background: url(images/1.jpg) no-repeat center top;
}
li {
list-style: none;
}
.baidu {
overflow: hidden;
margin: 100px auto;
background-color: #fff;
width: 410px;
padding-top: 3px;
}
.baidu li {
float: left;
margin: 0 1px;
cursor: pointer;
}
.baidu img {
width: 100px;
}
</style>
<body>
<ul class="baidu">
<li><img src="images/1.jpg"></li>
<li><img src="images/2.jpg"></li>
<li><img src="images/3.jpg"></li>
<li><img src="images/4.jpg"></li>
</ul>
<script>
// 1. 获取元素
var imgs = document.querySelector('.baidu').querySelectorAll('img');
// 2. 循环注册事件
for (let i = 0; i < imgs.length; i++) {
imgs[i].onclick = function() {
// this.src 就是我们点击图片的路径
// console.log(this.src);
// 把这个路径 this.src 给body 就可以了
document.body.style.backgroundImage = 'url(' + this.src + ')';
// document.body.style.backgroundImage = `url(${this.src})`;
}
}
</script>
</body>
效果图
4.4.7 案例-表格变色
<!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: 800px;
margin: 100px auto;
text-align: center;
border-collapse: collapse;
font-size: 14px;
}
thead tr {
height: 30px;
background-color: skyblue;
}
tbody tr {
height: 30px;
}
tbody td {
border-bottom: 1px solid #d7d7d7;
font-size: 12px;
color: blue;
}
.bg {
background-color: #d7d7d7;
}
</style>
</head>
<body>
<table>
<thead>
<tr>
<th>代码</th>
<th>名称</th>
<th>最新公布净值</th>
<th>累计净值</th>
<th>前单位净值</th>
<th>净值增长率</th>
</tr>
</thead>
<tbody>
<tr>
<td>000001</td>
<td>债券1</td>
<td>1.075</td>
<td>1.079</td>
<td>1.074</td>
<td>+0.047%</td>
</tr>
<tr>
<td>000002</td>
<td>债券2</td>
<td>0.903</td>
<td>3.386</td>
<td>0.000</td>
<td>+0.000%</td>
</tr>
<tr>
<td>000003</td>
<td>债券3</td>
<td>0.860</td>
<td>0.860</td>
<td>0.863</td>
<td>-0.382%</td>
</tr>
<tr>
<td>000004</td>
<td>债券4</td>
<td>1.034</td>
<td>1.088</td>
<td>1.034</td>
<td>+0.077%</td>
</tr>
<tr>
<td>000005</td>
<td>债券5</td>
<td>0.950</td>
<td>3.557</td>
<td>0.000</td>
<td>+0.000%</td>
</tr>
</tbody>
</table>
<script>
// 1.获取元素 获取的是 tbody 里面所有的行
var trs = document.querySelector('tbody').querySelectorAll('tr');
// 2. 利用循环绑定注册事件
for (let i = 0; i < trs.length; i++) {
// 3. 鼠标经过事件 onmouseover
trs[i].onmouseover = function() {
this.className = 'bg';
}
// 4. 鼠标离开事件 onmouseout
trs[i].onmouseout = function() {
this.className = '';
}
}
</script>
</body>
</html>
效果图
4.4.8 案例-全选反选
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.wrap {
width: 300px;
margin: 100px auto 0;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #c0c0c0;
width: 300px;
}
th,
td {
border: 1px solid #d0d0d0;
color: #404060;
padding: 10px;
}
th {
background-color: #09c;
font: bold 16px "微软雅黑";
color: #fff;
}
td {
font: 14px "微软雅黑";
}
tbody tr {
background-color: #f0f0f0;
}
tbody tr td:first-child {
text-align: center;
}
tbody tr:hover {
cursor: pointer;
background-color: #fafafa;
}
</style>
</head>
<body>
<div class="wrap">
<table>
<thead>
<tr>
<th>
<input type="checkbox" id="j_cbAll" />
</th>
<th>商品</th>
<th>价钱</th>
</tr>
</thead>
<tbody id="j_tb">
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPhone8</td>
<td>8000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Pro</td>
<td>5000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>iPad Air</td>
<td>2000</td>
</tr>
<tr>
<td>
<input type="checkbox" />
</td>
<td>Apple Watch</td>
<td>2000</td>
</tr>
</tbody>
</table>
</div>
<script>
// 1. 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
// 获取元素
var j_cbAll = document.getElementById('j_cbAll'); // 全选按钮
var j_tbs = document.getElementById('j_tb').getElementsByTagName('input'); // 下面所有的复选框
// 注册事件
j_cbAll.onclick = function() {
// this.checked 它可以得到当前复选框的选中状态如果是true 就是选中,如果是false 就是未选中
for (let i = 0; i < j_tbs.length; i++) {
j_tbs[i].checked = this.checked;
}
}
// 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。
for (let i = 0; i < j_tbs.length; i++) {
j_tbs[i].onclick = function() {
// flag 控制全选按钮是否选中
let flag = true;
// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中
for (let i = 0; i < j_tbs.length; i++) {
if (!j_tbs[i].checked) {
flag = false;
break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了
}
}
j_cbAll.checked = flag;
}
}
</script>
</body>
</html>
效果图
4.4.9 自定义属性的操作
- 获取属性值
element.属性
element.getAttribute(‘属性’);
区别:
- element.属性 获取内置属性值(元素本身自带的属性)
- element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
- 设置属性值
element.属性 = ‘值’
element.setAttribute(‘属性’, ‘值’);
区别:
- element.属性 设置内置属性值
- element.setAttribute(‘属性’); 主要设置自定义的属性 (标准)
- 移除属性
element.removeAttribute(‘属性’);
<body>
<div id="demo" index="1" class="nav"></div>
<script>
var div = document.querySelector('div');
// 1. 获取元素的属性值
// (1) element.属性
console.log(div.id);
console.log(div.index);
//(2) element.getAttribute('属性') get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 index
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
// 2. 设置元素属性值
// (1) element.属性= '值'
div.id = 'test';
div.className = 'navs';
// (2) element.setAttribute('属性', '值'); 主要针对于自定义属性
div.setAttribute('index', 2);
div.setAttribute('class', 'footer'); // class 特殊 这里面写的就是class 不是className
// 3 移除属性 removeAttribute(属性)
div.removeAttribute('index');
</script>
</body>
4.4.10 案例-tab栏切换(重点)
<!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;
}
.tab {
width: 978px;
margin: 100px auto;
}
.tab_list {
height: 39px;
border: 1px solid #ccc;
background-color: #f1f1f1;
}
.tab_list li {
float: left;
height: 39px;
line-height: 39px;
padding: 0 20px;
text-align: center;
cursor: pointer;
}
.tab_list .current {
background-color: #c81623;
color: #fff;
}
.item_info {
padding: 20px 0 0 20px;
}
.item {
display: none;
}
</style>
</head>
<body>
<div class="tab">
<div class="tab_list">
<ul>
<li class="current">商品介绍</li>
<li>规格与包装</li>
<li>售后保障</li>
<li>商品评价(50000)</li>
<li>手机社区</li>
</ul>
</div>
<div class="tab_con">
<div class="item" style="display: block;">
商品介绍模块内容
</div>
<div class="item">
规格与包装模块内容
</div>
<div class="item">
售后保障模块内容
</div>
<div class="item">
商品评价(50000)模块内容
</div>
<div class="item">
手机社区模块内容
</div>
</div>
</div>
<script>
// 获取元素
var tab_list = document.querySelector('.tab_list');
var lis = tab_list.querySelectorAll('li');
var items = document.querySelectorAll('.item');
// for循环绑定点击事件
for (let i = 0; i < lis.length; i++) {
// 开始给5个小li 设置索引号
lis[i].setAttribute('index', i);
lis[i].onclick = function() {
// 1. 模块选项卡,点击某一个,当前这一个底色会是红色,其余不变(排他思想) 修改类名的方式
// 清除 class 类
for (let i = 0; i < lis.length; i++) {
lis[i].className = '';
}
this.className = 'current'; // 留下自己
// 2. 显示内容模块
let index = this.getAttribute('index');
// 所有隐藏
for (let i = 0; i < items.length; i++) {
items[i].style.display = 'none';
}
items[index].style.display = 'block'; // 让对应的item 显示出来
}
}
</script>
</body>
</html>
效果图
4.5 H5自定义属性
<body>
<div getTime="20" data-index="2" data-list-name="andy"></div>
<script>
var div = document.querySelector('div');
// console.log(div.getTime);
console.log(div.getAttribute('getTime')); // 20
div.setAttribute('data-time', 20);
console.log(div.getAttribute('data-index')); // 2
console.log(div.getAttribute('data-list-name')); // andy
// 1.h5新增(ie11)的获取自定义属性的方法 它只能获取data-开头的
// (1) dataset 是一个集合里面存放了所有以data开头的自定义属性
console.log(div.dataset);
console.log(div.dataset.index); // 2
console.log(div.dataset['index']); // 2
// (2) 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法
console.log(div.dataset.listName); // andy
console.log(div.dataset['listName']); // andy
</script>
</body>
4.6 操作元素小结
五、节点操作
获取元素通常使用两种方式:
5.1 节点操作
一般地,节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType 为 1
- 属性节点 nodeType 为 2
- 文本节点 nodeType 为 3(文本节点包含文字、空格、换行等)
我们在实际开发中,节点操作主要操作的是元素节点
5.2 节点层级
- 父级节点
node.parentNode
- parentNode 属性可返回某节点的父节点,注意是最近的一个父节点
- 如果指定的节点没有父节点则返回 null
<body>
<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>
</body>
- 子节点
- parentNode.childNodes(标准)
var ul = document.querySelector('ul');
for (let i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
// ul.childNodes[i] 是元素节点
console.log(ul.childNodes[i]);
}
}
- 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>
<script>
// DOM 提供的方法(API)获取
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等
console.log(ul.childNodes); // NodeList(9) [text, li, text, li, text, li, text, li, text]
console.log(ul.childNodes[0].nodeType); // 3
console.log(ul.childNodes[1].nodeType); // 1
// 2. children 获取所有的子元素节点 也是我们实际开发常用的
console.log(ul.children); // HTMLCollection(4) [li, li, li, li]
</script>
</body>
- parentNode.firstChild
firstChild 返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。
- parentNode.lastChild
lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。
<body>
<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); // #text
console.log(ol.lastChild); // #text
// 2. firstElementChild 返回第一个子元素节点 ie9才支持
console.log(ol.firstElementChild); // <li>我是li1</li>
console.log(ol.lastElementChild); // <li>我是li5</li>
// 3. 实际开发的写法 既没有兼容性问题又返回第一个子元素
console.log(ol.children[0]); // <li>我是li1</li>
console.log(ol.children[ol.children.length - 1]); // <li>我是li5</li>
</script>
</body>
- 兄弟节点
- node.nextSibling
nextSibling 返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
- node.previousSibling
previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。
- node.nextElementSibling (ie9)
nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。
- node.previousElementSibling (ie9)
previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null。
// 解决兼容性-封装函数
function getNextElementSibling(element) {
let el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;
}
}
return null;
}
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
console.log(div.nextSibling); // #text
console.log(div.previousSibling); // #text
// 2. nextElementSibling 得到下一个兄弟元素节点
console.log(div.nextElementSibling); // <span>我是span</span>
console.log(div.previousElementSibling); // 没有兄弟,返回 null
</script>
</body>
5.2.1 案例-下拉菜单
<!DOCTYPE html>
<html lang="zh">
<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></title>
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
a {
color: #333;
font-size: 14px;
text-decoration: none;
}
.nav {
margin: 100px;
}
.nav>li {
float: left;
position: relative;
width: 80px;
height: 41px;
text-align: center;
border: 1px solid #FECC5B;
margin: 0 0 -1px -1px; // 去除边框合并
}
.nav li a {
display: block;
width: 100%;
height: 100%;
line-height: 41px;
}
.nav>li>a:hover {
background-color: #eee;
}
.nav ul {
display: none;
position: absolute;
top: 41px;
left: 0;
width: 100%;
}
.nav ul li {
border: 1px solid #FECC5B;
margin: -1px;
}
.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 (let i = 0; i < lis.length; i++) {
lis[i].onmouseover = function() {
this.style.borderBottom = '0'; // 清除列表与下拉菜单的边框重合
this.children[1].style.display = 'block';
}
lis[i].onmouseout = function() {
this.style.borderBottom = '1px solid #FECC5B';
this.children[1].style.display = 'none';
}
}
</script>
</body>
</html>
效果图
5.3 创建和添加节点
5.3.1 创建节点
document.createElement(‘tagName’)
document.createElement() 方法创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,是根据我们的需求动态生成的,所以我们也称为动态创建元素节点
5.3.2 添加节点
- node.appendChild(child)
node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的 after 伪元素。
- node.insertBefore(child, 指定元素)
node.insertBefore() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before 伪元素。
<body>
<ul>
<li>123</li>
</ul>
<script>
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素 类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3. 添加节点 node.insertBefore(child, 指定元素);
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 4. 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
</script>
</body>
5.3.3 案例-简单版发布留言
<!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 #666;
outline: none;
resize: none;
}
ul {
margin-top: 50px;
}
li {
width: 300px;
padding: 5px;
font-size: 14px;
border: 1px solid #666666;
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.innerText = text.value;
// (2) 添加元素
// ul.appendChild(li);
ul.insertBefore(li, ul.children[0]);
}
}
</script>
</body>
</html>
5.3.4 删除节点
node.removeChild(child)
node.removeChild() 方法从 DOM 中删除一个子节点,返回删除的节点。
<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>
5.3.5 案例-简单版删除留言
<!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;
font-size: 14px;
border: 1px solid #666666;
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 才能赋值
// javascript:; 阻止链接跳转
li.innerHTML = text.value + "<a href='javascript:;'>删除</a>";
// (2) 添加元素
ul.insertBefore(li, ul.children[0]);
// (3) 删除元素 删除的是当前链接的li 它的父亲
var as = document.querySelectorAll('a');
for (let i = 0; i < as.length; i++) {
as[i].onclick = function() {
// node.removeChild(child); 删除的是 li 当前a所在的li this.parentNode;
ul.removeChild(this.parentNode);
}
}
}
}
</script>
</body>
</html>
5.3.6 复制节点
node.cloneNode()
node.cloneNode() 方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点
<body>
<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>
</body>
5.3.7 案例-动态生成表格
<!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.先去准备好学生的数据
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: 87
}];
// 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行
var tbody = document.querySelector('tbody');
for (let i = 0; i < datas.length; i++) { // 外面的for循环管行 tr
// 1. 创建 tr行
let tr = document.createElement('tr');
tbody.appendChild(tr);
// 2. 行里面创建单元格(跟数据有关系的3个单元格) td 单元格的数量取决于每个对象里面的属性个数 for循环遍历对象 datas[i]
for (let k in datas[i]) { // 里面的for循环管列 td
// 创建单元格
let td = document.createElement('td');
// 把对象里面的属性值 datas[i][k] 给 td
td.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)
}
}
// for(var k in obj) {
// k 得到的是属性名
// obj[k] 得到是属性值
// }
</script>
</body>
</html>
5.3.8 三种动态创建元素的区别
<body>
<button>点击</button>
<p>abc</p>
<div class="inner"></div>
<div class="create"></div>
<script>
// 三种创建元素方式区别
// 1. document.write() 创建元素 如果页面文档流加载完毕,再调用这句话会导致页面重绘
// var btn = document.querySelector('button');
// btn.onclick = function() {
// document.write('<div>123</div>');
// }
// 会导致页面重绘,类似于 window.onload
// window.onload = function() {
// document.write('<div>123</div>');
// }
// 2. innerHTML 创建元素
// 2.1 拼接字符串 效率低不推荐
var inner = document.querySelector('.inner');
// for (var i = 0; i <= 100; i++) {
// inner.innerHTML += '<a href="#">百度</a>'
// }
// 2.2 数组创建 将所需元素添加进数组,在使用分隔符分隔 效率最高但结构稍微复杂
var arr = [];
for (let i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3. document.createElement() 创建元素
// 效率稍低但结构清晰
var create = document.querySelector('.create');
for (let i = 0; i <= 100; i++) {
let a = document.createElement('a');
create.appendChild(a);
}
</script>
</body>
// innerHTML 拼接效率
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(); // 几秒 千位
// innerHTML 数组效率
// 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(); // 几毫秒 个位
// createElement 效率
// 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(); // 几十毫秒 十位
六、DOM重点核心总结
6.1 创建
6.2 增
6.3 删
6.4 改
6.5 查
6.6 属性操作
6.7 事件操作(部分)
给元素注册事件, 采取 事件源.事件类型 = 事件处理程序