JavaScript APIs学习day2--DOM!!
1. 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. 获取元素
2.1 如何获取页面元素
DOM在我们实际开发中主要用来操作元素。
获取页面中的元素可以使用以下几种方式:根据 ID 获取、根据标签名获取、通过 HTML5 新增的方法获取、特殊元素获取。
2.2 根据 ID 获取
使用 getElementById() 方法可以获取带有 ID 的元素对象。
使用 console.dir() 可以打印我们获取的元素对象,更好的查看对象里面的属性和方法
<body><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);//div#time</script>
</body>
2.3 根据标签名获取
使用 getElementsByTagName() 方法可以返回带有指定标签名的对象的集合。
注意:
1. 因为得到的是一个对象的集合, 所以我们想要操作里面的元素就需要遍历。
2. 得到元素对象是动态的
父元素必须是单个对象(必须指明是哪个元素对象),获取的时候不包括父元素自己。
<body><ul><li>知否知否,应是等你好久11</li><li>知否知否,应是等你好久22</li><li>知否知否,应是等你好久33</li><li>知否知否,应是等你好久44</li></ul><ol id="ol"><li>生僻字</li><li>生僻字</li><li>生僻字</li><li>生僻字</li></ol><script>// 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的var lis = document.getElementsByTagName('li');console.log(lis);//HTMLCollection(8) [li, li, li, li, li, li, li, li]console.log(lis[0]);//<li>知否知否,应是等你好久11</li>// 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式for (var i = 0; i < lis.length; i++) {console.log(lis[i]);}// 3. 如果页面中只有一个li 返回的还是伪数组的形式 // 4. 如果页面中没有这个元素 返回的是空的伪数组的形式// 5. element.getElementsByTagName('标签名'); 父元素必须是指定的单个元素// var ol = document.getElementsByTagName('ol'); // [ol]// console.log(ol[0].getElementsByTagName('li'));//如果不指定id,ol[0]必须指定var ol = document.getElementById('ol');console.log(ol.getElementsByTagName('li'));</script>
</body>
2.4 通过 HTML5 新增的方法获取
<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);//HTMLCollection(2) [div.box, div.box]// 2. querySelector 返回指定选择器的第一个元素对象 切记 里面的选择器需要加符号 .box #navvar firstBox = document.querySelector('.box');console.log(firstBox);//<div class="box">盒子1</div>var nav = document.querySelector('#nav');console.log(nav);//<div id="nav">。。。</div>var li = document.querySelector('li');console.log(li); //<li>首页</li>// 3. querySelectorAll()返回指定选择器的所有元素对象集合var allBox = document.querySelectorAll('.box');console.log(allBox);//NodeList(2) [div.box, div.box]var lis = document.querySelectorAll('li');console.log(lis);//NodeList(2) [li, li]</script>
</body>
注意:
querySelector 和 querySelectorAll里面的选择器需要加符号,比如:document.querySelector('#nav');
2.5 获取特殊元素( body, html)
获取body元素
var bodyEle = document.body;console.log(bodyEle);console.dir(bodyEle);
获取html元素
var htmlEle = document.documentElement;console.log(htmlEle);
3. 事件基础
3.1 事件概述
JavaScript 使我们有能力创建动态页面,而事件是可以被 JavaScript 侦测到的行为。
简单理解: 触发--- 响应机制。
网页中的每个元素都可以产生某些可以触发 JavaScript 的事件, 例如, 我们可以在用户点击某按钮时产生一个事件,然后去执行某些操作。
3.2 事件三要素
- 事件源 (谁)
- 事件类型 (什么事件)
- 事件处理程序 (做啥)
案例:点击按钮弹出警示框
页面中有一个按钮,当鼠标点击按钮的时候, 弹出“你好” 警示框
<body><button id="btn">唐伯虎</button><script>//(1) 事件源 事件被触发的对象 谁 按钮var btn = document.getElementById('btn');//(2) 事件类型 如何触发 什么事件 比如鼠标点击(onclick) 还是鼠标经过 还是键盘按下//(3) 事件处理程序 通过一个函数赋值的方式 完成btn.onclick = function() {alert('点秋香');}</script>
</body>
3.3 执行事件的步骤
- 获取事件源
- 注册事件(绑定事件)
- 添加事件处理程序(采取函数赋值形式)
<body><div>123</div><script>// 执行事件步骤// 点击div 控制台输出 我被选中了// 1. 获取事件源var div = document.querySelector('div');// 2.绑定事件 注册事件// div.onclick // 3.添加事件处理程序 div.onclick = function() {console.log('我被选中了');}</script>
</body>
3.4 常见的鼠标事件
4. 操作元素
JavaScript 的 DOM 操作可以改变网页内容、 结构和样式, 我们可以利用 DOM 操作元素来改变元素里面的内容 、属性等。注意以下都是属性.
4.1 改变元素内容
element.innerText从起始位置到终止位置的内容, 但它去除 html 标签, 同时空格和换行也会去掉
element.innerHTML起始位置到终止位置的全部内容, 包括 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,p {width: 300px;height: 30px;line-height: 30px;color: #fff;background-color: pink;}</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.innerText = '2019-6-6';div.innerHTML = getDate();}function getDate() {var date = new Date();// 我们写一个 2025年 5月 19日 星期三var year = date.getFullYear();var month = date.getMonth() + 1;var dates = date.getDate();var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];var day = date.getDay();return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];}// 我们元素可以不用添加事件var p = document.querySelector('p');p.innerHTML = getDate();</script>
</body></html>
innerText 和 innerHTML的区别:
// 1. innerText 不识别html标签 非标准 去除空格和换行var div = document.querySelector('div');div.innerText = '<strong>今天是:</strong> 2019';// 2. innerHTML 识别html标签 W3C标准 保留空格和换行的div.innerHTML = '<strong>今天是:</strong> 2019';
// 这两个属性是可读写的 可以获取元素里面的内容var p = document.querySelector('p');console.log(p.innerText);console.log(p.innerHTML);
4.2 常用元素的属性操作
- innerText、 innerHTML 改变元素内容
- src、 href
- id、 alt、 title
<body><button id="ldh">刘德华</button><button id="zxy">张学友</button> </br><img src="images/ldh.jpg" alt="" title="刘德华"><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>
案例: 分时显示不同图片,显示不同问候语
根据不同时间,页面显示不同图片,同时显示不同的问候语。
如果上午时间打开页面,显示上午好,显示上午的图片。
如果下午时间打开页面,显示下午好,显示下午的图片。
如果晚上时间打开页面,显示晚上好,显示晚上的图片。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>img {width: 300px;}</style>
</head>
<body><img src="images/s.gif" alt=""><div>上午好</div><script>// 1.获取元素var img = document.querySelector('img');var div = document.querySelector('div');// 2.得到当前的小时数var data = new Date();var h = data.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>
</html>
4.3 表单元素的属性操作
利用 DOM 可以操作如下表单元素的属性: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>
案例:仿京东显示密码
点击按钮将密码框切换为文本框, 并可以查看密码明文。
核心思路:
- 点击眼睛按钮,把密码框类型改为文本框就可以看见里面的密码
- 一个按钮两个状态,点击一次,切换为文本框,继续点击一次切换为密码框
- 算法:利用一个flag变量,来判断flag的值,如果是1 就切换为文本框, flag 设置为0,如果是0 就切换为密码框, flag设置为1。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>.box {position: relative;width: 400px;border: 1px solid #ccc;margin: 100px auto;}.box input {width: 370px;height: 30px;border: 0;/* 使得输入时密码框没有黑色 */outline: none;}.box img {position: absolute;width: 24px;top: 2px;right: 2px;}</style>
</head>
<body><div class="box"><label for=""><img src="images/close.png" alt="" id="eye"></label><input type="password" class="" 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';flag = 1;eye.src = 'images/open.png';} else {pwd.type = 'password';flag = 0;eye.src = 'images/close.png';} }</script>
</body>
</html>
4.4 样式属性操作
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
- element.style 行内样式操作
- element.className 类名样式操作
<!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: 100px;height: 100px;background-color: pink;}.change {background-color: purple;color: #fff;font-size: 25px;margin-top: 100px;}</style>
</head><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></html>
注意:
- JS 里面的样式采取驼峰命名法 比如 fontSize、 backgroundColor
- JS 修改 style 样式操作,产生的是行内样式, CSS 权重比较高
- 如果样式修改较多,可以采取操作类名方式更改元素样式
- class因为是个保留字,因此使用className来操作元素类名属性
- className 会直接更改元素的类名,会覆盖原先的类名
案例: 密码框格式提示错误信息
用户如果离开密码框,里面输入个数不是6~16,则提示错误信息,否则提示输入正确信息.
- 首先判断的事件是表单失去焦点 onblur
- 如果输入正确则提示正确的信息颜色为绿色小图标变化
- 如果输入不是6到16位,则提示错误信息颜色为红色 小图标变化
- 因为里面变化样式较多,我们采取className修改样式
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><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>var ipt = document.querySelector('.ipt');var message = document.querySelector('.message');ipt.onblur = function() {//根据表单里面值的长度 ipt.value.lengthif(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>
案例: 淘宝点击关闭二维码
当鼠标点击二维码关闭按钮的时候,则隐藏整个二维码。
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><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: 70px;}.close-btn{position: absolute;top: -0.5px;left: -16px;width: 14px;height: 14px;border: 1px solid #ccc;font-family: Arial, Helvetica, sans-serif;}</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>
案例: 循环精灵图背景
可以利用 for 循环设置一组元素的精灵图背景。
- 首先精灵图图片排列有规律的
- 核心思路: 利用for循环 修改精灵图片的 背景位置 background-position
- 让循环里面的 i 索引号 * 44 就是每个图片的y坐标
<!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>// 1. 获取元素 所有的小li var lis = document.querySelectorAll('li');for (var i = 0; i < lis.length; i++) {// 让索引号 乘以 44 就是每个li 的背景y坐标 index就是我们的y坐标var index = i * 44;lis[i].style.backgroundPosition = '0 -' + index + 'px';}</script>
</body></html>
案例:显示隐藏文本框内容
当鼠标点击文本框时, 里面的默认文字隐藏,当鼠标离开文本框时, 里面的文字显示。
- 首先表单需要2个新事件,获得焦点 onfocus 失去焦点 onblur
- 如果获得焦点, 判断表单里面内容是否为默认文字,如果是默认文字,就清空表单内容
- 如果失去焦点, 判断表单内容是否为空,如果为空,则表单内容改为默认文字
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>input {color: #999;outline: none;}</style>
</head>
<body><input type="text" value="手机"><script>// 1. 获取元素var text = document.querySelector('input');// 2. 注册事件 获得焦点事件onfocustext.onfocus = function() {// console.log('得到了焦点');if(this.value == '手机'){this.value = '';} this.style.color = '#333';}// 2. 注册事件 获得焦点事件onblurtext.onblur = function(){// console.log('失去焦点');if(this.value ===''){this.value = '手机';}this.style.color = '#999';}</script>
</body>
</html>
4.5 操作元素总结
案例:开关灯案例
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><button id="btn">开关灯</button><script>var btn = document.getElementById('btn');var flag = 1;btn.onclick = function() {if(flag == 1) {document.body.style.backgroundColor = 'black';flag = 0;} else {document.body.style.backgroundColor = 'white';flag = 1;}}</script>
</body>
</html>
4.5 排他思想
如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:
1. 所有元素全部清除样式(干掉其他人)
2. 给当前元素设置样式 (留下我自己)
3. 注意顺序不能颠倒,首先干掉其他人,再设置自己
<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 (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';}}//2. 首先先排除其他人,然后才设置自己的样式 这种排除其他人的思想我们成为排他思想</script>
</body>
案例:百度换肤
① 这个案例练习的是给一组元素注册事件
② 给4个小图片利用循环注册点击事件
③ 当我们点击了这个图片,让我们页面背景改为当前的图片
④ 核心算法: 把当前图片的src 路径取过来,给 body 做为背景即可
<!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 {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>
</head><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');// console.log(imgs);// 2. 循环注册事件 for (var i = 0; i < imgs.length; i++) {imgs[i].onclick = function() {// this.src 就是我们点击图片的路径 images/2.jpg// console.log(this.src);// 把这个路径 this.src 给body 就可以了document.body.style.backgroundImage = 'url(' + this.src + ')';}}</script>
</body></html>
案例:表格隔行变色
① 用到新的鼠标事件 鼠标经过 onmouseover 鼠标离开 onmouseout
② 核心思路:鼠标经过 tr 行,当前的行变背景颜色, 鼠标离开去掉当前的背景颜色
③ 注意: 第一行( thead里面的行)不需要变换颜色,因此我们获取的是 tbody 里面的行
<!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: pink;}</style>
</head><body><table><thead><tr><th>代码</th><th>名称</th><th>最新公布净值</th><th>累计净值</th><th>前单位净值</th><th>净值增长率</th></tr></thead><tbody><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr><tr><td>003526</td><td>农银金穗3个月定期开放债券</td><td>1.075</td><td>1.079</td><td>1.074</td><td>+0.047%</td></tr></tbody></table><script>// 1.获取元素 获取的是 tbody 里面所有的行var trs = document.querySelector('tbody').querySelectorAll('tr');// 2. 利用循环绑定注册事件for (var i = 0; i < trs.length; i++) {// 3. 鼠标经过事件 onmouseovertrs[i].onmouseover = function() {// console.log(11);this.className = 'bg';}// 4. 鼠标离开事件 onmouseouttrs[i].onmouseout = function() {this.className = '';}}</script>
</body></html>
案例:表单全选取消全选案例
业务需求:
1. 点击上面全选复选框,下面所有的复选框都选中(全选)
2. 再次点击全选复选框,下面所有的复选框都不中选(取消全选)
3. 如果下面复选框全部选中,上面全选按钮就自动选中
4. 如果下面复选框有一个没有选中,上面全选按钮就不选中
5. 所有复选框一开始默认都没选中状态
① 全选和取消全选做法: 让下面所有复选框的checked属性(选中状态) 跟随 全选按钮即可
② 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的,上面全选就不选中。
③ 可以设置一个变量,来控制全选是否选中
<!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 "微软雅黑";text-align: center;}tbody tr {background-color: #f0f0f0;}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>iPhone14pm</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 就是未选中console.log(this.checked);for (var i = 0; i < j_tbs.length; i++) {j_tbs[i].checked = this.checked;}}// 2. 下面复选框需要全部选中, 上面全选才能选中做法: 给下面所有复选框绑定点击事件,每次点击,都要循环查看下面所有的复选框是否有没选中的,如果有一个没选中的, 上面全选就不选中。for (var i = 0; i < j_tbs.length; i++) {j_tbs[i].onclick = function() {// flag 控制全选按钮是否选中var flag = true;// 每次点击下面的复选框都要循环检查者4个小按钮是否全被选中for (var i = 0; i < j_tbs.length; i++) {if (!j_tbs[i].checked) {flag = false;break; // 退出for循环 这样可以提高执行效率 因为只要有一个没有选中,剩下的就无需循环判断了}}j_cbAll.checked = flag;}}</script>
</body></html>
4.6 自定义属性的操作
4.6.1 获取属性值
- element.属性 获取属性值
- element.getAttribute('属性')
区别:
- element.属性 获取内置属性值(元素本身自带的属性)
- element.getAttribute(‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性
4.6.2 设置属性值
- element.属性 = ‘值’ 设置内置属性值
- element.setAttribute('属性', '值')
区别:
- element.属性 设置内置属性值
- element.setAttribute(‘属性’); 主要设置自定义的属性 (标准)
4.6.3 移除属性
element.removeAttribute('属性');
<body><div id="demo" index="1" class="nav"></div><script>var div = document.querySelector('div');// 1. 获取元素的属性值// (1) element.属性console.log(div.id);//(2) element.getAttribute('属性') get得到获取 attribute 属性的意思 我们程序员自己添加的属性我们称为自定义属性 indexconsole.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>
案例: tab 栏切换(重点案例)
当鼠标点击上面相应的选项卡(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 (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>
</body></html>
4.7 H5自定义属性
自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。
自定义属性获取是通过getAttribute(‘属性’) 获取。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。
H5给我们新增了自定义属性:
4.7.1 设置H5自定义属性
H5规定自定义属性data-开头做为属性名并且赋值。
比如 <div data-index=“1”></div>或者使用 JS 设置element.setAttribute(‘data-index’, 2)
4.7.2 获取H5自定义属性
- 兼容性获取 element.getAttribute(‘data-index’);
- H5新增 element.dataset.index 或者 element.dataset[‘index’] ,ie11才开始支持
<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'));div.setAttribute('data-time', 20);console.log(div.getAttribute('data-index'));console.log(div.getAttribute('data-list-name'));// h5新增的获取自定义属性的方法 它只能获取data-开头的// dataset 是一个集合里面存放了所有以data开头的自定义属性console.log(div.dataset);console.log(div.dataset.index);console.log(div.dataset['index']);// 如果自定义属性里面有多个-链接的单词,我们获取的时候采取 驼峰命名法console.log(div.dataset.listName);console.log(div.dataset['listName']);</script>
</body>
5. 节点操作
5.1 为什么学节点操作
获取元素通常使用两种方式:
这两种方式都可以获取元素节点,我们后面都会使用,但是节点操作更简单。
5.2 节点概述
网页中的所有内容都是节点(标签、属性、文本、 注释等),在DOM 中,节点使用 node 来表示。HTML DOM 树中的所有节点均可通过 JavaScript 进行访问, 所有 HTML 元素(节点)均可被修改,也可以创建或删除。
一般地,节点至少拥有nodeType(节点类型)、 nodeName(节点名称)和nodeValue(节点值)这三个基本属性。
- 元素节点 nodeType 为 1
- 属性节点 nodeType 为 2
- 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
我们在实际开发中,节点操作主要操作的是元素节点。
5.3 节点层级
利用 DOM 树可以把节点划分为不同的层级关系,常见的是父子兄层级关系。
5.3.1 父级节点
- node.parentNode 属性可返回某节点的父节点, 注意是最近的一个父节点
- 如果指定的节点没有父节点则返回 null
<script>// 1. 父节点 parentNodevar erweima = document.querySelector('.erweima');// var box = document.querySelector('.box');// 得到的是离元素最近的父级节点(亲爸爸) 如果找不到父节点就返回为 nullconsole.log(erweima.parentNode);</script>
5.3.2 子节点
- 1 parentNode.childNodes 返回包含指定节点的子节点的集合,该集合为即时更新的集合。
注意: 返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes
- 2 parentNode.children 是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返回 ( 这个是我们重点掌握的),实际开发常用的。
虽然children 是一个非标准,但是得到了各个浏览器的支持,可以放心使用。
<!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>我是li</li><li>我是li</li><li>我是li</li><li>我是li</li></ol><div class="demo"><div class="box"><span class="erweima">×</span></div></div><script>// DOM 提供的方法(API)获取var ul = document.querySelector('ul');var lis = ul.querySelectorAll('li');// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等console.log(ul.childNodes);//[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);//[li, li, li, li]</script>
</body></html>
- 3 parentNode.firstChild 返回第一个子节点,找不到则返回null。同样也是包含所有的节点。
- 4 parentNode.lastChild 返回最后一个子节点,找不到则返回null。同样也是包含所有的节点。
- 5 firstElementChild 返回第一个子元素节点, 找不到则返回null。
- 6 lastElementChild 返回最后一个子元素节点, 找不到则返回null。
注意: 方法5 6有兼容性问题, IE9 以上才支持。
实际开发中, firstChild 和 lastChild 包含其他节点,操作不方便,而 firstElementChild 和
lastElementChild 又有兼容性问题,那么我们如何获取第一个子元素节点或最后一个子元素节点?
解决方案:
1. 如果想要第一个子元素节点, 可以使用 parentNode.chilren[0]
2. 如果想要最后一个子元素节点, 可以使用 parentNode.chilren[parentNode.chilren.length - 1]。
<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);//#textconsole.log(ol.lastChild);//#text// 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>
</body>
案例:下拉菜单
<!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.3.3 兄弟节点
1. node.nextSibling 返回当前元素的下一个兄弟节点, 找不到则返回null。包含所有的节点。
2. node.previousSibling 返回当前元素上一个兄弟节点,找不到则返回null。包含所有的节点。
3. node.nextElementSibling 返回当前元素下一个兄弟元素节点, 找不到则返回null。
4. node.previousElementSibling 返回当前元素上一个兄弟节点, 找不到则返回null。、
<body><div>我是div</div><span>我是span</span><script>var div = document.querySelector('div');// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等console.log(div.nextSibling);//#textconsole.log(div.previousSibling);//#text// 2. nextElementSibling 得到下一个兄弟元素节点console.log(div.nextElementSibling);//<span>我是span</span>console.log(div.previousElementSibling);//null</script>
</body>
注意: 这两个方法有兼容性问题, IE9 以上才支持。
如何解决兼容性问题 ? 答:自己封装一个兼容性的函数
function getNextElementSibling(element) {
var el = element;
while (el = el.nextSibling) {
if (el.nodeType === 1) {
return el;}}
return null;
}
5.4 创建节点
document.createElement('tagName') 方法创建由 tagName 指定的 HTML 元素。 因为这些元素原先不存在,是根据我们的需求动态生成的, 所以我们也称为动态创建元素节点。
- node.appendChild(child) 方法将一个节点添加到指定父节点的子节点列表末尾。 类似于 CSS 里面的after 伪元素。
- node.insertBefore(child, 指定元素) 方法将一个节点添加到父节点的指定子节点前面。 类似于 CSS 里面的 before伪元素。
<body><ul><li>123</li></ul><script>// 1. 创建节点元素节点var li = document.createElement('li');// 2. 添加节点 node.appendChild(child) node 父级 child 是子级 后面追加元素 类似于数组中的pushvar 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>
案例:简单版发布留言案例
① 核心思路: 点击按钮之后,就动态创建一个li,添加到ul 里面。
② 创建li 的同时,把文本域里面的值通过li.innerHTML 赋值给 li
③ 如果想要新的留言后面显示就用 appendChild 如果想要前面显示就用insertBefore
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><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>var btn = document.querySelector('button');var text = document.querySelector('textarea');var ul = document.querySelector('ul') ;btn.onclick = function() {if(text.value == '') {alert('你没有输入内容');return false;}else {var li = document.createElement('li');li.innerHTML = text.value;// ul.appendChild(li);ul.insertBefore(li, ul.children[0]);}}</script>
</body>
</html>
5.5 删除节点
- node.removeChild(child) 方法从 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>
案例:删除留言案例
① 当我们把文本域里面的值赋值给li 的时候,多添加一个删除的链接
② 需要把所有的链接获取过来,当我们点击当前的链接的时候,删除当前链接所在的li
③ 阻止链接跳转需要添加 javascript:void(0); 或者 javascript:;
<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() {// node.removeChild(child); 删除的是 li 当前a所在的li this.parentNode;ul.removeChild(this.parentNode);}}}}</script>
5.6 复制节点(克隆节点)
- node.cloneNode() 方法返回调用该方法的节点的一个副本。 也称为克隆节点/拷贝节点。
注意:
1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身, 不克隆里面的子节点。
2. 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
<body><ul><li>1111</li><li>2</li><li>3</li></ul><script>var ul = document.querySelector('ul');var lili = ul.children[0].cloneNode(true);ul.appendChild(lili);</script>
</body>
案例:动态生成表格
<!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: 0}];// 2. 往tbody 里面创建行: 有几个人(通过数组的长度)我们就创建几行var tbody = document.querySelector('tbody');for (var i = 0; i < datas.length; i++) { // 外面的for循环管行 tr// 1. 创建 tr行var tr = document.createElement('tr');tbody.appendChild(tr);// 2. 行里面创建单元格(跟数据有关系的3个单元格) td 单元格的数量取决于每个对象里面的属性个数 for循环遍历对象 datas[i]for (var k in datas[i]) { // 里面的for循环管列 td// 创建单元格 var td = document.createElement('td');// 把对象里面的属性值 datas[i][k] 给 td // console.log(datas[i][k]);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.7 三种动态创建元素区别
- document.write()
- element.innerHTML
- document.createElement()
区别:
- document.write 是直接将内容写入页面的内容流, 但是文档流执行完毕,则它会导致页面全部重绘
- innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
- innerHTML 创建多个元素效率更高(不要拼接字符串,采取数组形式拼接),结构稍微复杂
- createElement() 创建多个元素效率稍低一点点,但是结构更清晰
总结: 不同浏览器下, innerHTML 效率要比 creatElement 高。
<body><button>点击</button><p>abc</p><div class="inner"></div><div class="create"></div><script>// window.onload = function() {// document.write('<div>123</div>');// }// 三种创建元素方式区别 // 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>
</body>
6. DOM 重点核心
文档对象模型( Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言( HTML或者XML)的标准编程接口。
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
- 对于JavaScript,为了能够使JavaScript操作HTML, JavaScript就有了一套自己的dom编程接口。
- 对于HTML, dom使得html形成一棵dom树. 包含 文档、元素、节点。
关于dom操作,我们主要针对于元素的操作。主要创建、增、删、改、查、属性操作、事件操作。
6.1 创建
- document.write
- innerHTML
- createElement
6.2 增
- appendChild
- insertBefore
6.3 删
- removeChild
6.4 改
主要修改dom的元素属性, dom元素的内容、属性, 表单的值等。
- 修改元素属性: src、 href、 title等
- 修改普通元素内容: innerHTML 、 innerText
- 修改表单元素: value、 type、 disabled等
- 修改元素样式: style、 className
6.5 查
主要获取查询dom的元素。
- DOM提供的API 方法: getElementById、 getElementsByTagName 古老用法 不太推荐
- H5提供的新方法: querySelector、 querySelectorAll 提倡
- 利用节点操作获取元素: 父(parentNode)、子(children)、兄(previousElementSibling、nextElementSibling) 提倡。
6.6 属性操作
主要针对于自定义属性。
- setAttribute:设置dom的属性值
- getAttribute:得到dom的属性值
- removeAttribute移除属性
6.7 事件操作
给元素注册事件, 采取 事件源.事件类型 = 事件处理程序