JS-WEBAPI学习笔记
day1
const和let的使用
默认都使用const,除非你明确知道值会发生改变
1 2 3 4 5 6 7 8 9 10 11 12
| const num = 1 num = 2
const arr = ['1','2','3'] arr.push('4') arr = []
const person = { name: 张三, age: 18 } person.gender = 男
|
介绍
知道 ECMAScript 与 JavaScript 的关系,Web APIs 是浏览器扩展的功能。
严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。
ECMAScript 运行在浏览器中然后再结合 Web APIs 才是真正的 JavaScript,Web APIs 的核心是 DOM 和 BOM。
扩展阅读:ECMAScript 规范在不断的更新中,存在多个不同的版本,早期的版本号采用数字顺序编号如 ECMAScript3、ECMAScript5,后来由于更新速度较快便采用年份做为版本号,如 ECMAScript2017、ECMAScript2018 这种格式,ECMAScript6 是 2015 年发布的,常叫做 EMCAScript2015。
关于 JavaScript 历史的扩展阅读。
知道 DOM 相关的概念,建立对 DOM 的初步认识,学习 DOM 的基本操作,体会 DOM 的作用
DOM(Document Object Model)是将整个 HTML 文档的每一个标签元素视为一个对象,这个对象下包含了许多的属性和方法,通过操作这些属性或者调用这些方法实现对 HTML 的动态更新,为实现网页特效以及用户交互提供技术支撑。
简言之 DOM 是用来动态修改 HTML 的,其目的是开发网页特效及用户交互。
观察一个小例子:
上述的例子中当用户分分别点击【开始】或【结束】按钮后,通过右侧调试窗口可以观察到 html 标签的内容在不断的发生改变,这便是通过 DOM 实现的。
概念
DOM 树
1 2 3 4 5 6 7 8 9 10 11 12 13
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>标题</title> </head> <body> 文本 <a href="">链接名</a> <div id="" class="">文本</div> </body> </html>
|
如下图所示,将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树,文档树直观的体现了标签与标签之间的关系。
DOM 节点
节点是文档树的组成部分,每一个节点都是一个 DOM 对象,主要分为元素节点、属性节点、文本节点等。
- 【元素节点】其实就是 HTML 标签,如上图中
head
、div
、body
等都属于元素节点。
- 【属性节点】是指 HTML 标签中的属性,如上图中
a
标签的 href
属性、div
标签的 class
属性。
- 【文本节点】是指 HTML 标签的文字内容,如
title
标签中的文字。
- 【根节点】特指
html
标签。
- 其它…
document
document
是 JavaScript 内置的专门用于 DOM 的对象,该对象包含了若干的属性和方法,document
是学习 DOM 的核心。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script>
console.log(document.documentElement);
console.log(document.body);
document.write('Hello World!'); </script>
|
上述列举了 document
对象的部分属性和方法,我们先对 document
有一个整体的认识。
获取DOM对象
- querySelector 满足条件的第一个元素
- querySelectorAll 满足条件的元素集合 返回伪数组
- 了解其他方式
- getElementById
- getElementsByTagName
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <body> <div class="box" id="box">aaa</div> <div class="else">bbb</div> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> <script> document.querySelector('div'); document.querySelector('.box'); document.querySelector('#box'); const li = document.querySelector('ul li:first-child') console.log(li); const ul = document.querySelectorAll('ul li') console.log(ul); </script> </body>
|
总结:
- document.getElementById 专门获取元素类型节点,根据标签的
id
属性查找
- 任意 DOM 对象都包含 nodeType 属性,用来检检测节点类型
操作元素内容
通过修改 DOM 的文本内容,动态改变网页的内容。
innerText
将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。
1 2 3 4 5 6
| <script> const intro = document.querySelector('.intro') </script>
|
innerHTML
将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。
1 2 3 4 5 6
| <script> const intro = document.querySelector('.intro') intro.innerHTML = '嗨~ 我叫韩梅梅!' intro.innerHTML = '<h4>嗨~ 我叫韩梅梅!</h4>' </script>
|
总结:如果文本内容中包含 html
标签时推荐使用 innerHTML
,否则建议使用 innerText
属性。
抽奖案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <div class="wrapper"> <strong>年会抽奖</strong> <h1>一等奖:<span id="one">???</span></h1> <h3>二等奖:<span id="two">???</span></h3> <h5>三等奖:<span id="three">???</span></h5> <h6>参与奖:<span id="other">???</span></h6> </div> <script> const arr = ['张志立', '安东尼', '马奎尔', '霍伊伦', '腾哈格', '佩德里'] const one = Math.floor(Math.random() * arr.length) document.querySelector('#one').innerHTML = arr[one] arr.splice(one, 1)
const two = Math.floor(Math.random() * arr.length) document.querySelector('#two').innerHTML = arr[two] arr.splice(two, 1)
const three = Math.floor(Math.random() * arr.length) document.querySelector('#three').innerHTML = arr[three] arr.splice(three, 1)
document.querySelector('#other').innerHTML = arr </script>
|
操作元素属性
有3种方式可以实现对属性的修改:
常用属性修改
- 直接能过属性名修改,最简洁的语法
1 2 3 4 5 6 7 8
| <script> const pic = document.querySelector('.pic') pic.src = './images/lion.webp' pic.width = 400; pic.alt = '图片不见了...' </script>
|
控制样式属性
- 应用【修改样式】,通过修改行内样式
style
属性,实现对样式的动态修改。
通过元素节点获得的 style
属性本身的数据类型也是对象,如 box.style.color
、box.style.width
分别用来获取元素节点 CSS 样式的 color
和 width
的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>练习 - 修改样式</title> </head> <body> <div class="box">随便一些文本内容</div> <script> const box = document.querySelector('.intro') box.style.color = 'red' box.style.width = '300px' box.style.backgroundColor = 'pink' </script> </body> </html>
|
任何标签都有 style
属性,通过 style
属性可以动态更改网页标签的样式,如要遇到 css
属性中包含字符 -
时,要将 -
去掉并将其后面的字母改成大写,如 background-color
要写成 box.style.backgroundColor
- 操作类名(className) 操作CSS
如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>练习 - 修改样式</title> <style> .pink { background: pink; color: hotpink; } </style> </head> <body> <div class="box">随便一些文本内容</div> <script> const box = document.querySelector('.intro') box.className = 'pink' </script> </body> </html>
|
注意:
1.由于class是关键字, 所以使用className去代替
2.className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
- 通过 classList 操作类控制CSS
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> div { width: 200px; height: 200px; background-color: pink; }
.active { width: 300px; height: 300px; background-color: hotpink; margin-left: 100px; } </style> </head>
<body>
<div class="one"></div> <script> let box = document.querySelector('div') box.classList.toggle('one') </script> </body>
</html>
|
操作表单元素属性
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
正常的有属性有取值的跟其他的标签属性没有任何区别
获取:DOM对象.属性名
设置:DOM对象.属性名= 新值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title>
</head>
<body> <input type="text" value="请输入"> <button disabled>按钮</button> <input type="checkbox" name="" id="" class="agree"> <script> let input = document.querySelector('input') input.value = '小米手机' input.type = 'password'
let btn = document.querySelector('button') btn.disabled = false let checkbox = document.querySelector('.agree') checkbox.checked = false </script> </body>
</html>
|
自定义属性
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、selected
自定义属性:
在html5中推出来了专门的data-自定义属性
在标签上一律以data-开头
在DOM对象上一律以dataset对象方式获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title>
</head>
<body> <div data-id="1"> 自定义属性 </div> <script> let div = document.querySelector('div') console.log(div.dataset.id) </script> </body>
</html>
|
间歇函数
知道间歇函数的作用,利用间歇函数创建定时任务。
setInterval
是 JavaScript 中内置的函数,它的作用是间隔固定的时间自动重复执行另一个函数,也叫定时器函数。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <script> function repeat() { console.log('不知疲倦的执行下去....') }
let n = setInterval(repeat, 1000) clearInterval(n) </script>
|
定时器案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <textarea name="" id="" cols="30" rows="10"> 用户注册协议 欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。 【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看 </textarea> <br> <button class="btn" disabled>我已经阅读用户协议(5)</button> <script> const btn = document.querySelector('.btn') let time = 5 let n = setInterval(() => { btn.innerHTML = `我已经阅读用户协议(${--time})` if (time === 0) { clearInterval(n) btn.disabled = false btn.innerHTML = '同意' } }, 1000)
</script> </body>
|
day2
事件
事件是编程语言中的术语,它是用来描述程序的行为或状态的,一旦行为或状态发生改变,便立即调用一个函数。
例如:用户使用【鼠标点击】网页中的一个按钮、用户使用【鼠标拖拽】网页中的一张图片
事件监听
结合 DOM 使用事件时,需要为 DOM 对象添加事件监听,等待事件发生(触发)时,便立即调用一个函数。
addEventListener
是 DOM 对象专门用来添加事件监听的方法,它的两个参数分别为【事件类型】和【事件回调】。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>事件监听</title> </head> <body> <h3>事件监听</h3> <p id="text">为 DOM 元素添加事件监听,等待事件发生,便立即执行一个函数。</p> <button id="btn">点击改变文字颜色</button> <script> const btn = document.querySelector('#btn')
btn.addEventListener('click', function () { console.log('等待事件被触发...') let text = document.getElementById('text') text.style.color = 'red' })
</script> </body> </html>
|
完成事件监听分成3个步骤:
- 获取 DOM 元素
- 通过
addEventListener
方法为 DOM 节点添加事件监听
- 等待事件触发,如用户点击了某个按钮时便会触发
click
事件类型
- 事件触发后,相对应的回调函数会被执行
大白话描述:所谓的事件无非就是找个机会(事件触发)调用一个函数(回调函数)。
随机点名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <body> <h2>随机点名</h2> <div class="box"> <span>名字是:</span> <div class="qs">这里显示姓名</div> </div> <div class="btns"> <button class="start">开始</button> <button class="end">结束</button> </div>
<script> const arr = ['马超', '黄忠', '赵云', '关羽', '张飞'] const start = document.querySelector('.start') const end = document.querySelector('.end') const qs = document.querySelector('.qs') let n = 0 start.addEventListener('click', () => { n = setInterval(() => { qs.innerHTML = arr[parseInt(Math.random() * arr.length)] }, 100) }) end.addEventListener('click', () => { clearInterval(n) }) </script> </body>
|
事件类型
click
译成中文是【点击】的意思,它的含义是监听(等着)用户鼠标的单击操作,除了【单击】还有【双击】dblclick
1 2 3 4 5 6 7 8 9 10 11
| <script> btn.addEventListener('dblclick', function () { console.log('等待事件被触发...'); const text = document.querySelector('.text') text.style.color = 'red' })
</script>
|
结论:【事件类型】决定了事件被触发的方式,如 click
代表鼠标单击,dblclick
代表鼠标双击。
事件处理程序
addEventListener
的第2个参数是函数,这个函数会在事件被触发时立即被调用,在这个函数中可以编写任意逻辑的代码,如改变 DOM 文本颜色、文本内容等。
1 2 3 4 5 6 7 8 9 10 11 12
| <script> btn.addEventListener('dblclick', function () { console.log('等待事件被触发...') const text = document.querySelector('.text') text.style.color = 'red' text.style.fontSize = '20px' }) </script>
|
结论:【事件处理程序】决定了事件触发后应该执行的逻辑。
事件类型
将众多的事件类型分类可分为:鼠标事件、键盘事件、表单事件、焦点事件等,我们逐一展开学习。
鼠标事件
鼠标事件是指跟鼠标操作相关的事件,如单击、双击、移动等。
1.`mouseenter 监听鼠标是否移入 DOM 元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <h3>鼠标事件</h3> <p>监听与鼠标相关的操作</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box');
box.addEventListener('mouseenter', function () { this.innerText = '鼠标移入了...'; this.style.cursor = 'move'; }) </script> </body>
|
2.`mouseleave 监听鼠标是否移出 DOM 元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <body> <h3>鼠标事件</h3> <p>监听与鼠标相关的操作</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box');
box.addEventListener('mouseleave', function () { this.innerText = '鼠标移出了...'; }) </script> </body>
|
键盘事件
keydown 键盘按下触发
keyup 键盘抬起触发
焦点事件
focus 获得焦点
blur 失去焦点
搜索框获得失去焦点案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| <style> * { margin: 0; padding: 0; box-sizing: border-box; }
ul {
list-style: none; }
.mi { position: relative; width: 223px; margin: 100px auto; }
.mi input { width: 223px; height: 48px; padding: 0 10px; font-size: 14px; line-height: 48px; border: 1px solid #e0e0e0; outline: none; }
.mi .search { border: 1px solid #ff6700; }
.result-list { display: none; position: absolute; left: 0; top: 48px; width: 223px; border: 1px solid #ff6700; border-top: 0; background: #fff; }
.result-list a { display: block; padding: 6px 15px; font-size: 12px; color: #424242; text-decoration: none; }
.result-list a:hover { background-color: #eee; } </style>
</head>
<body> <div class="mi"> <input type="search" placeholder="小米笔记本"> <ul class="result-list"> <li><a href="#">全部商品</a></li> <li><a href="#">小米11</a></li> <li><a href="#">小米10S</a></li> <li><a href="#">小米笔记本</a></li> <li><a href="#">小米手机</a></li> <li><a href="#">黑鲨4</a></li> <li><a href="#">空调</a></li> </ul> </div> <script> const input = document.querySelector('[type=search]') const ul = document.querySelector('.result-list') input.addEventListener('focus', function () { ul.style.display = 'block' input.classList.add('search') }) input.addEventListener('blur', function () { ul.style.display = 'none' input.classList.remove('search') }) </script> </body>
|
文本框输入事件
input
轮播图案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>轮播图点击切换</title> <style> * { box-sizing: border-box; }
.slider { width: 560px; height: 400px; overflow: hidden; }
.slider-wrapper { width: 100%; height: 320px; }
.slider-wrapper img { width: 100%; height: 100%; display: block; }
.slider-footer { height: 80px; background-color: rgb(100, 67, 68); padding: 12px 12px 0 12px; position: relative; }
.slider-footer .toggle { position: absolute; right: 0; top: 12px; display: flex; }
.slider-footer .toggle button { margin-right: 12px; width: 28px; height: 28px; appearance: none; border: none; background: rgba(255, 255, 255, 0.1); color: #fff; border-radius: 4px; cursor: pointer; }
.slider-footer .toggle button:hover { background: rgba(255, 255, 255, 0.2); }
.slider-footer p { margin: 0; color: #fff; font-size: 18px; margin-bottom: 10px; }
.slider-indicator { margin: 0; padding: 0; list-style: none; display: flex; align-items: center; }
.slider-indicator li { width: 8px; height: 8px; margin: 4px; border-radius: 50%; background: #fff; opacity: 0.4; cursor: pointer; }
.slider-indicator li.active { width: 12px; height: 12px; opacity: 1; } </style> </head>
<body> <div class="slider"> <div class="slider-wrapper"> <img src="./images/slider01.jpg" alt="" /> </div> <div class="slider-footer"> <p>对人类来说会不会太超前了?</p> <ul class="slider-indicator"> <li class="active"></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> <li></li> </ul> <div class="toggle"> <button class="prev"><</button> <button class="next">></button> </div> </div> </div> <script> const data = [ { url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' }, { url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' }, { url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' }, { url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' }, { url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' }, { url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' }, { url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' }, { url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' }, ] const img = document.querySelector('.slider-wrapper img') const p = document.querySelector('.slider-footer p') const footer = document.querySelector('.slider-footer') const next = document.querySelector('.next') let i = 0
next.addEventListener('click', function () { i++ i = i >= data.length ? 0 : i toggle() })
const prev = document.querySelector('.prev') prev.addEventListener('click', function () { i-- i = i < 0 ? data.length - 1 : i toggle() })
function toggle () { img.src = data[i].url p.innerHTML = data[i].title footer.style.backgroundColor = data[i].color document.querySelector('.slider-indicator .active').classList.remove('active') document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active') }
let timerId = setInterval(function () { next.click() }, 1000)
const slider = document.querySelector('.slider') slider.addEventListener('mouseenter', function () { clearInterval(timerId) })
slider.addEventListener('mouseleave', function () { if (timerId) clearInterval(timerId) timerId = setInterval(function () { next.click() }, 1000) }) </script> </body>
</html>
|
事件对象
任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <body> <h3>事件对象</h3> <p>任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。</p> <hr> <div class="box"></div> <script> const box = document.querySelector('.box')
box.addEventListener('click', function (e) { console.log('任意事件类型被触发后,相关信息会以对象形式被记录下来...');
console.log(e) }) </script> </body>
|
事件回调函数的【第1个参数】即所谓的事件对象,通常习惯性的将这个对数命名为 event
、ev
、ev
。
接下来简单看一下事件对象中包含了哪些有用的信息:
ev.type
当前事件的类型
ev.clientX/Y
光标相对浏览器窗口的位置
ev.offsetX/Y
光标相于当前 DOM 元素的位置
注:在事件回调函数内部通过 window.event 同样可以获取事件对象。
评论案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| <body> <div class="wrapper"> <i class="avatar"></i> <textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea> <button>发布</button> </div> <div class="wrapper"> <span class="total">0/10字</span> </div> <div class="list"> <div class="item" style="display: none;"> <i class="avatar"></i> <div class="info"> <p class="name">清风徐来</p> <p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p> <p class="time">2022-10-10 20:29:21</p> </div> </div> </div> <script> const tx = document.querySelector('#tx') const total = document.querySelector('.total') tx.addEventListener('focus', () => { total.style.opacity = 1 }) tx.addEventListener('blur', () => { total.style.opacity = 0 }) tx.addEventListener('input', () => { let length = tx.value.length; if (length > 10) { tx.value = tx.value.substring(0, 10); length = 10; }
total.innerHTML = `${length}/10字`; }); tx.addEventListener('keyup', (e) => { if (e.key === 'Enter') { if (tx.value.trim() !== '') { document.querySelector('.list .item').style.display = 'block' document.querySelector('.list .item .text').innerHTML = tx.value.trim() tx.value = '' } } }) </script> </body>
|
环境对象
能够分析判断函数运行在不同环境中 this 所指代的对象。
环境对象指的是函数内部特殊的变量 this
,它代表着当前函数运行时所处的环境。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <script> function sayHi() { console.log(this); }
let user = { name: '张三', sayHi: sayHi } let person = { name: '李四', sayHi: sayHi }
sayHi() window.sayHi()
user.sayHi() person.sayHi() </script>
|
结论:
this
本质上是一个变量,数据类型为对象
- 函数的调用方式不同
this
变量的值也不同
- 【谁调用
this
就是谁】是判断 this
值的粗略规则
- 函数直接调用时实际上
window.sayHi()
所以 this
的值为 window
tab切换案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <body> <div class="tab"> <div class="tab-nav"> <h3>每日特价</h3> <ul> <li><a class="active" href="javascript:;">精选</a></li> <li><a href="javascript:;">美食</a></li> <li><a href="javascript:;">百货</a></li> <li><a href="javascript:;">个护</a></li> <li><a href="javascript:;">预告</a></li> </ul> </div> <div class="tab-content"> <div class="item active"><img src="./images/tab00.png" alt="" /></div> <div class="item"><img src="./images/tab01.png" alt="" /></div> <div class="item"><img src="./images/tab02.png" alt="" /></div> <div class="item"><img src="./images/tab03.png" alt="" /></div> <div class="item"><img src="./images/tab04.png" alt="" /></div> </div> </div> <script> const arr = document.querySelectorAll('.tab-nav a') const item = document.querySelectorAll('.tab-content item') for (let i = 0; i < arr.length; i++) { arr[i].addEventListener('mouseenter', function () { document.querySelector('.tab-nav .active').classList.remove('active') this.classList.add('active') document.querySelector('.tab-content .active').classList.remove('active') document.querySelector(`.tab-content .item:nth-child(${i + 1})`).classList.add('active') }) } </script> </body>
|
事件委托优化版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <body> <div class="tab"> <div class="tab-nav"> <h3>每日特价</h3> <ul> <li><a class="active" href="javascript:;" data-id="1">精选</a></li> <li><a href="javascript:;" data-id="2">美食</a></li> <li><a href="javascript:;" data-id="3">百货</a></li> <li><a href="javascript:;" data-id="4">个护</a></li> <li><a href="javascript:;" data-id="5">预告</a></li> </ul> </div> <div class="tab-content"> <div class="item active"><img src="./images/tab00.png" alt="" /></div> <div class="item"><img src="./images/tab01.png" alt="" /></div> <div class="item"><img src="./images/tab02.png" alt="" /></div> <div class="item"><img src="./images/tab03.png" alt="" /></div> <div class="item"><img src="./images/tab04.png" alt="" /></div> </div> </div> <script> document.querySelector('.tab-nav ul').addEventListener('click', function (e) { if (e.target.tagName === 'A') { document.querySelector('.tab-nav .active').classList.remove('active') e.target.classList.add('active') let id = e.target.dataset.id document.querySelector('.tab-content .active').classList.remove('active') document.querySelector(`.tab-content .item:nth-child(${id})`).classList.add('active') } }) </script> </body>
|
回调函数
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <script> function foo(arg) { console.log(arg); }
foo(10); foo('hello world!'); foo(['html', 'css', 'javascript']);
function bar() { console.log('函数也能当参数...'); } foo(bar); </script>
|
函数 bar
做参数传给了 foo
函数,bar
就是所谓的回调函数了!!!
我们回顾一下间歇函数 setInterval
1 2 3 4 5 6 7
| <script> function fn() { console.log('我是回调函数...'); } setInterval(fn, 1000); </script>
|
fn
函数做为参数传给了 setInterval
,这便是回调函数的实际应用了,结合刚刚学习的函数表达式上述代码还有另一种更常见写法。
1 2 3 4 5 6
| <script> setInterval(function () { console.log('我是回调函数...'); }, 1000); </script>
|
结论:
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数做为回调函数比较常见
CSS伪类选择器实现全选
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| <body> <table> <tr> <th class="allCheck"> <input type="checkbox" name="" id="checkAll"> <span class="all">全选</span> </th> <th>商品</th> <th>商家</th> <th>价格</th> </tr> <tr> <td> <input type="checkbox" name="check" class="ck"> </td> <td>小米手机</td> <td>小米</td> <td>¥1999</td> </tr> <tr> <td> <input type="checkbox" name="check" class="ck"> </td> <td>小米净水器</td> <td>小米</td> <td>¥4999</td> </tr> <tr> <td> <input type="checkbox" name="check" class="ck"> </td> <td>小米电视</td> <td>小米</td> <td>¥5999</td> </tr> </table> <script> const checkAll = document.querySelector('#checkAll') const cks = document.querySelectorAll('.ck') checkAll.addEventListener('click', function () { for (let i = 0; i < cks.length; i++) { cks[i].checked = this.checked } }) for (let i = 0; i < cks.length; i++) { cks[i].addEventListener('click', function () { checkAll.checked = document.querySelectorAll('.ck:checked').length === cks.length }) } </script> </body>
|
day3
事件流
事件流是对事件执行过程的描述,了解事件的执行过程有助于加深对事件的理解,提升开发实践中对事件运用的灵活度。
如上图所示,任意事件被触发时总会经历两个阶段:【捕获阶段】和【冒泡阶段】。
简言之,捕获阶段是【从父到子】的传导过程,冒泡阶段是【从子向父】的传导过程。
捕获和冒泡
了解了什么是事件流之后,我们来看事件流是如何影响事件执行的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| <body> <h3>事件流</h3> <p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p> <div class="outer"> <div class="inner"> <div class="child"></div> </div> </div> <script> const outer = document.querySelector('.outer'); const inner = document.querySelector('.inner'); const child = document.querySelector('.child'); document.documentElement.addEventListener('click', function () { console.log('html...') }) document.body.addEventListener('click', function () { console.log('body...') })
outer.addEventListener('click', function () { console.log('outer...') }) outer.addEventListener('click', function () { console.log('inner...') }) outer.addEventListener('click', function () { console.log('child...') }) </script> </body>
|
执行上述代码后发现,当单击事件触发时,其祖先元素的单击事件也【相继触发】,这是为什么呢?
结合事件流的特征,我们知道当某个元素的事件被触发时,事件总是会先经过其祖先才能到达当前元素,然后再由当前元素向祖先传递,事件在流动的过程中遇到相同的事件便会被触发。
再来关注一个细节就是事件相继触发的【执行顺序】,事件的执行顺序是可控制的,即可以在捕获阶段被执行,也可以在冒泡阶段被执行。
如果事件是在冒泡阶段执行的,我们称为冒泡模式,它会先执行子盒子事件再去执行父盒子事件,默认是冒泡模式。
如果事件是在捕获阶段执行的,我们称为捕获模式,它会先执行父盒子事件再去执行子盒子事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <body> <h3>事件流</h3> <p>事件流是事件在执行时的底层机制,主要体现在父子盒子之间事件的执行上。</p> <div class="outer"> <div class="inner"></div> </div> <script> const outer = document.querySelector('.outer') const inner = document.querySelector('.inner')
outer.addEventListener('click', function () { console.log('outer...') }, true) outer.addEventListener('click', function () { console.log('inner...') }, true) </script> </body>
|
结论:
addEventListener
第3个参数决定了事件是在捕获阶段触发还是在冒泡阶段触发
addEventListener
第3个参数为 true
表示捕获阶段触发,false
表示冒泡阶段触发,默认值为 false
- 事件流只会在父子元素具有相同事件类型时才会产生影响
- 绝大部分场景都采用默认的冒泡模式(其中一个原因是早期 IE 不支持捕获)
阻止冒泡
阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <body> <h3>阻止冒泡</h3> <p>阻止冒泡是指阻断事件的流动,保证事件只在当前元素被执行,而不再去影响到其对应的祖先元素。</p> <div class="outer"> <div class="inner"> <div class="child"></div> </div> </div> <script> const outer = document.querySelector('.outer') const inner = document.querySelector('.inner') const child = document.querySelector('.child')
outer.addEventListener('click', function () { console.log('outer...') })
inner.addEventListener('click', function (ev) { console.log('inner...')
ev.stopPropagation() })
child.addEventListener('click', function (ev) { console.log('child...')
ev.stopPropagation() }) </script> </body>
|
结论:事件对象中的 ev.stopPropagation
方法,专门用来阻止事件冒泡。
鼠标经过事件:
mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果 (推荐)
事件委托
事件委托是利用事件流的特征解决一些现实开发需求的知识技巧,主要的作用是提升程序效率。
大量的事件监听是比较耗费性能的,如下代码所示
1 2 3 4 5 6 7 8 9 10 11
| <script> const buttons = document.querySelectorAll('table button');
for(let i = 0; i <= buttons.length; i++) { buttons.addEventListener('click', function () { }) } </script>
|
利用事件流的特征,可以对上述的代码进行优化,事件的的冒泡模式总是会将事件流向其父元素的,如果父元素监听了相同的事件类型,那么父元素的事件就会被触发并执行,正是利用这一特征对上述代码进行优化,如下代码所示:
1 2 3 4 5 6 7 8 9 10
| <script> let buttons = document.querySelectorAll('table button'); let parents = document.querySelector('table'); parents.addEventListener('click', function () { console.log('点击任意子元素都会触发事件...'); }) </script>
|
我们的最终目的是保证只有点击 button 子元素才去执行事件的回调函数,如何判断用户点击是哪一个子元素呢?
事件对象中的属性 target
或 srcElement
属性表示真正触发事件的元素,它是一个元素类型的节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <script> const buttons = document.querySelectorAll('table button') const parents = document.querySelector('table') parents.addEventListener('click', function (ev) { if(ev.target.tagName === 'BUTTON') { } }) </script>
|
优化过的代码只对祖先元素添加事件监听,相比对 10000 个元素添加事件监听执行效率要高许多!!!
阻止默认行为
1 2 3
| document.querySelector('a').addEventListener('click',function(e){ e.preventDefault() })
|
其他事件
页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
有些时候需要等页面资源全部处理完了做一些事情
事件名:load
监听页面所有资源加载完毕:
1 2 3
| window.addEventListener('load', function() { })
|
元素滚动事件
滚动条在滚动的时候持续触发的事件
1 2 3
| window.addEventListener('scroll', function() { })
|
页面滚动事件
1 2 3
| window.addEventListener('scorll',function(){ document.documentElement.scorllTop })
|
页面尺寸事件
会在窗口尺寸改变的时候触发事件:
1 2 3
| window.addEventListener('resize', function() { })
|
元素尺寸与位置
获取元素的自身宽高、包含元素自身设置的宽高、padding、border
offsetWidth和offsetHeight
获取出来的是数值,方便计算
注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0