js事件
- 什么是事件? 一个事件的组成:
- 触发谁的事件: 事件源
- 触发什么事件: 事件类型
- 触发后做什么: 事件处理函数
btn.onclick = function () {}分析: - 触发谁的事件 -> btn -> 事件源就是 btn
- 触发什么事件 -> onclick -> 事件类型就是 click
- 触发后做什么事 -> function () {} -> 这个事件的处理函数
- 常见的事件(了解)
- 浏览器事件
- onload: 页面全部资源加载完毕
- onscroll: 浏览器页面滚动的时候
- 鼠标事件
- 键盘事件
- 表单事件
1. 鼠标事件
<style>
* {
padding: 0;
margin: 0;
}
div {
width: 400px;
height: 400px;
background-color: pink;
}
.sBox {
width: 100px;
height: 100px;
background-color: green;
}
</style>
<div>
<div class="sBox"></div>
</div>
// 0. 获取元素
var oDiv = document.querySelector('div')
// 1. 左键单击
oDiv.onclick = function () {
console.log('单击元素时触发')
}
// 2. 双击事件 300ms 内连续点击两次鼠标
oDiv.ondblclick = function () {
console.log('双击元素时触发')
}
// 3. 右键事件
oDiv.oncontextmenu = function () {
console.log('鼠标右键单击时触发')
}
// 4. 鼠标按下事件 鼠标左键按下的时候触发(哪怕鼠标没有抬起也会触发)
oDiv.onmousedown = function () {
console.log('鼠标按下时触发')
}
// 5. 鼠标抬起事件 鼠标左键抬起的时候触发
oDiv.onmouseup = function () {
console.log('鼠标抬起时触发')
}
// 6. 鼠标移入事件 鼠标移入元素的时候触发 注意: 移入子级盒子时, 也会触发
oDiv.onmouseover = function () {
console.log('onmouseover 移入事件触发')
}
// 7. 鼠标移出事件 鼠标移出元素的时候触发 注意: 移入子级盒子, 也会触发
oDiv.onmouseout = function () {
console.log('onmouseout 移出事件触发')
}
// 8. 鼠标移入事件2 鼠标移入元素的时候触发 注意: 移入子级盒子时, 并不会触发
oDiv.onmouseenter = function () {
console.log('onmouseenter 移入事件触发')
}
// 9. 鼠标移出事件2 鼠标移出元素的时候触发 注意: 移出鼠标到子盒子时并不会触发
oDiv.onmouseleave = function () {
console.log('onmouseleave 移出事件触发')
}
// 10. 鼠标移动事件 鼠标在 oDiv 元素内部 移动的时候会触发
oDiv.onmousemove = function () {
console.log('鼠标移动事件触发~~~')
}
2. 键盘事件
键盘事件
-
- document 当前文档
-
- input 输入框
// 1. 键盘抬起事件
document.onkeyup = function () {
/console.log('任意按下一个按键然后抬起时会触发')
}
// 2. 键盘按下事件
document.onkeydown = function () {
console.log('键盘任意一个按键被按下')
}
// 3. 键盘按下抬起事件
document.onkeypress = function () {
console.log('键盘任意按键按下抬起时触发')
}
3. 表单事件
<input type="text">
<script>
// 0. 获取元素
var inp = document.querySelector('input')
// 1. 获得焦点事件
inp.onfocus = function () {
console.log('当前文本框获得焦点')
}
// 2. 失去焦点事件
inp.onblur = function () {
console.log('当前文本框失去焦点')
}
// 3. 文本框内容改变时触发
inp.onchange = function () {
console.log('当前文本框内容发生改变')
}
// 4. 文本框输入内容时触发
inp.oninput = function () {
console.log('当前文本框正在输入内容')
}
</script>
4. 选项卡案例
5. 事件对象
- 什么是事件对象:当触发一个事件以后, 对该事件的一些描述信息,比如:=> 鼠标点击时的坐标=> 触发键盘事件时按下的那个按键,每一个事件都会有一个对象来描述这信息, 我们就把这个对象叫做事件对象
- 浏览器给我们一个 "黑盒子", 叫做
window.event就是对事件信息的所有描述 - 语法:
元素.onclick = function () { console.log(window.event.X轴的坐标点信息) console.log(window.event.Y轴的坐标点信息) } - 缺点: 这个东西有兼容性问题, 在 低版本的 IE 里边能用, 但是在高版本 IE 和 chrome 里边是不好使 所以我们需要换一种使用方式:就是在每一个事件处理函数的形参位置, 默认第一个就是事件对象
6. 点击事件光标位置的获取
<style>
body {
height: 5000px;
}
div {
width: 300px;
height: 300px;
padding: 20px;
border: 10px solid #333;
margin: 50px;
position: fixed;
top: 300px;
}
</style>
<div></div>
<script>
/**
* 点击事件的光标位置
*
* 坐标一定是一个相对的数据
*
* 比如:
* => 相对于事件源 (就是我们点击的那个元素)
* => 相对于页面
* => 相对于浏览器窗口
*/
// 0. 获取元素
var box = document.querySelector('div')
box.onclick = function (e) {
// 1. 相对于事件源 的鼠标坐标点 相对于点击的元素的边框内测开始计算
console.log('==========================')
console.log('相对于事件源 X 轴', e.offsetX)
console.log('相对于事件源 Y 轴', e.offsetY)
// 2. 相对于页面
console.log('==========================')
console.log('相对于页面 X 轴', e.pageX)
console.log('相对于页面 Y 轴', e.pageY)
// 3. 相对于浏览器窗口
console.log('==========================')
console.log('相对于浏览器窗口 X 轴', e.clientX)
console.log('相对于浏览器窗口 Y 轴', e.clientY)
}
</script>
7. 获取键盘按键
键盘的每一个按键都有一个自己的编码,我们可以通过 事件对象.keyCode 来获取,e.keyCode已经被移除了, 但是很多主流浏览器还支持使用,现在推荐我们使用: e.key
<input type="text">
// 0. 获取元素
var inp = document.querySelector('input')
inp.onkeyup = function (e) {
// console.log(e.ctrlKey)
// console.log(e.altKey)
// console.log(e.keyCode) // q === 81
if (e.altKey && e.keyCode === 81) {
console.log('按下了组合按键 alt + q')
}
}
8. onXXX绑定事件的弊端
- 问题:
- 我们现在注册事件就是通过 onXXX,只能给元素注册一个事件, 如果写了第二个, 那么第一个就会被覆盖掉
- 解决:
- 如果想要两个事件全都存在, 我们可以使用 事件监听的方式去给元素绑定事件,使用
addEventListener去给元素通过事件监听的方式,在 IE 中需要使用 attachEvent (了解即可)
<div></div>
// 0. 获取元素
var box = document.querySelector('div')
// 1. 给 box 元素添加一个点击事件
box.onclick = function () {
console.log('点击事件 1, 触发了~~~')
}
// 2. 给 box 元素添加第二个点击事件
box.onclick = function () {
console.log('点击事件 2, 触发了~~~')
}
9. 事件监听
addEventListener
- 语法:
元素.addEventListener('事件类型', 事件处理函数, 第三个形参) - 第三个形参先欠着, 后续会详细讲解, 因为有默认值, 所以我们暂时先不传递第三个形参
- 注意: 这里的事件类型, 全部都一样不需要加 on
- 执行顺序会按照我们的注册的顺序执行(也就是代码书写的顺序)
<div></div>
// 0. 获取元素
var box = document.querySelector('div')
box.addEventListener('click', function () {
console.log('绑定的第一个事件')
})
box.addEventListener('click', function () {
console.log('绑定的第三个事件')
})
box.addEventListener('click', function () {
console.log('绑定的第二个事件')
})
10. 渲染表格案例
需求:
-
- 接收到后端给到的数据
-
- 将数据渲染到页面 思路:
-
- 将数组内的数据, 转换为我们需要的字符串
-
- 通过 innerHTML 插入到页面渲染出来
- `` 包裹起来的内容和 '' 包裹起来的一样, 都是一个字符串
- 两者的区别:
- ``可以换行, ''不能换行
- `` 内部 书写 ${} 这个大括号内部可以书写变量
- 通过 innerHTML 插入到页面渲染出来
//html:
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>班级</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
//script:
// 1. 写一个数组, 模拟接收到了后端给到的数据
var users = [
{
id: 1,
name: '张三',
age: 67,
gender: '男',
classRoom: '2XX'
},
{
id: 2,
name: '李四',
age: 66,
gender: '女',
classRoom: '2XX'
},
{
id: 3,
name: '王五',
age: 18,
gender: '男',
classRoom: '2XX'
}
]
// 0. 获取元素
var tBodyEle = document.querySelector('tbody')
// 1. 将数组内的数据, 转换为我们需要的字符串
var str = ''
users.forEach(function (item) {
// console.log(item)
// str += '<tr><td>1</td><td>张三1</td><td>男</td><td>67</td><td>2XX</td></tr>'
str += `<tr>
<td> ${item.id} </td>
<td> ${item.name} </td>
<td> ${item.gender} </td>
<td> ${item.age} </td>
<td> ${item.classRoom} </td>
</tr>`
})
// 2. 通过 innerHTML 插入到页面渲染
tBodyEle.innerHTML = str
11. 事件传播
假设我们在浏览器内用一个宽高 500px盒子, 内部有一个水平垂直都居中的子级盒子宽高为100px当我点击在 子盒子上的时候, 其实也是点击在了这个 父盒子的身上,这种情况就叫做 事件传播
- => 当元素触发一个事件的时候, 这个元素的父级节点也会触发相同的事件, 父元素的父元素也会触发相同的事件
- => 点击了子盒子, 会触发子盒子的点击事件
- => 也是点在了父盒子上, 也会触发父盒子的点击事件
- => 也是点在了 body 上, 也会触发 body 的点击事件
- => 也是点在了 html 上, 也会触发 html 的点击事件
- => 也是点在了 document 上, 也会触发 document 的点击事件
- => 也是点在了 window 上, 也会触发 window 的点击事件
- => 页面上任何一个元素触发事件, 都会一层一层的最终导致 window 的相同事件触发
注意点:
-
- 只会传播同类事件, 如果是点击事件, 那么只会触发父级或者父级的父级他们注册的点击事件, 其他类型的事件不会得到触发
-
- 只会从点击的元素开始, 按照 html 的结构, 逐层向上触发同类型的事件
-
- 内部元素不管有没有该事件, 只要上层元素有该事件, 那么上层元素的事件就会触发
//html:
<div class="box">
<div class="sBox"></div>
</div>
//css:
<style>
.box {
width: 500px;
height: 500px;
background-color: pink;
}
.sBox {
width: 100px;
height: 100px;
background-color: green;
}
</style>
//script:
// 0. 获取元素
var sBox = document.querySelector('.sBox')
var box = document.querySelector('.box')
var oBody = document.body
// sBox.onclick = function () {
// console.log('我是 内层 DIV, 我的点击事件被触发了')
// }
box.onclick = function () {
console.log('我是 外层 DIV, 我的点击事件被触发了')
}
oBody.onclick = function () {
console.log('我是 body, 我的点击事件被触发了')
}
12. 冒泡与捕获(面试题) 与目标
-
目标: 你点击在那个元素上, 那么这个事件的目标 就是这个元素
-
冒泡: 就是从 目标 的事件处理函数开始, 依次向上, 一直到 window 的事件处理函数触发 也就是说从下向上的执行 事件处理函数
-
捕获: 就是从 window 的事件处理函数开始, 依次向下, 一直到 目标 的事件处理函数触发 也就是说从上向下的执行 事件处理函数
-
区别: 就是在事件的传播中, 多个同类型的事件处理函数的执行顺序不同, 仅此而已
//html:
<div class="box">
<div class="sBox"></div>
</div>
//css:
<style>
.box {
width: 500px;
height: 500px;
background-color: pink;
}
.sBox {
width: 100px;
height: 100px;
background-color: green;
}
</style>
//script:
// 0. 获取元素
var sBox = document.querySelector('.sBox')
var box = document.querySelector('.box')
var oBody = document.body
// 1. 按照事件冒泡的形式传播
sBox.onclick = function () {
console.log('我是 内层 DIV, 我的点击事件被触发了')
}
box.onclick = function () {
console.log('我是 外层 DIV, 我的点击事件被触发了')
}
oBody.onclick = function () {
console.log('我是 body, 我的点击事件被触发了')
}
/**
* 2. 按照事件捕获的形式传播
*
* addEventListener 第三个参数就是决定当前传播方式为 捕获还是冒泡
*
* 默认第三个参数为 false, 代表传播方式为 冒泡
*
* 如果想按照捕获的形式传播, 那么给第三个参数 传递一个 true
*/
sBox.addEventListener('click', function () {
console.log('我是 内层 DIV, 我的点击事件被触发了')
}, true)
box.addEventListener('click', function () {
console.log('我是 外层 DIV, 我的点击事件被触发了')
}, true)
oBody.addEventListener('click', function () {
console.log('我是 body, 我的点击事件被触发了')
}, true)
13. 事件委托
就是要把我自己做的事, 委托给别人, 帮我完成.因为我们的冒泡机制, 点击子元素的时候, 也会同步触发父元素的相同事件,所以我们可以把子元素的事件委托给父元素来做,点击子元素的时候, 不管子元素有没有点击事件, 只要父元素有点击事件, 那么就可以触发父元素的点击事件
target
- 这个属性是事件对象里的属性, 表示你点击的目标
- 这个 target 兼容性有问题, 在 IE 浏览器内 需要使用 srcElement 语法: e.srcElement (了解即可)
var oUl = document.querySelector('ul')
oUl.onclick = function (e) {
if (e.target.nodeName === 'LI') {
console.log('我是 li 标签, 我被点击了~~~')
}
}
事件委托的优点
- 页面上本身没有 li, 通过 代码添加了一些 li
- 这些 li 是没有点击事件的, 每次动态的添加 li, 还需要从新给 li 绑定一次点击事件
- 这时候使用事件委托就比较合适
- 因为: 新加进来的 li 也是 ul 的子元素, 点击的时候也可以触发 ul 的点击事件
14. 案例
需求: 给 Ul 内部的 所有 li 添加一个 点击事件, 但是不能使用事件委托
<style>
ul {
width: 500px;
background-color: pink;
}
li {
width: 200px;
background-color: green;
margin-top: 5px;
}
</style>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
// 0. 获取元素
// var lis = [...document.querySelectorAll('li')]
var oUl = document.querySelector('ul')
// 1. 遍历数组, 给目前结构内所有的 li 添加一个点击事件
// lis.forEach(function (item) {
// item.onclick = function () {
// console.log('我点击了 li')
// }
// })
// 1.1 利用事件委托解决
oUl.onclick = function (e) {
if (e.target.nodeName === 'LI') {
console.log('我点击了 li')
}
}
// 2. 创建一个 li 节点并添加到 ul 内
var newLi = document.createElement('li')
newLi.innerText = '我是通过 JS 创建的 li 节点'
oUl.appendChild(newLi)
15. 默认行为
- 默认行为:不用我们注册, 但是自己存在的事情
- 比如:鼠标右键单击, 会弹出一个菜单; 点击 a 标签后, 自己会跳转到页面,这些不需要我们注册就是能实现的事情, 我们叫做 默认事件
- 阻止默认事件
- 不希望浏览器执行默认事件时, 比如 点击 a 标签不跳转页面, 那么就需要阻止默认事件
- 两种方式:
-
e.preventDefault()-> 非 IE 浏览器
-
e.returnValue = false-> IE 浏览器 (了解即可)
-
<a href="https://www.baidu.com/">点击之后跳转百度</a>
var oA = document.querySelector('a')
oA.onclick = function (e) {
console.log('该函数执行, 拦截掉了 a 标签的默认行为')
e.preventDefault()
}
16. 开光案例
/**
* 功能:
* 点击一下按钮, 让按钮变成 X秒后可以重新执行, 并且在 5秒内不能重新执行
*
* 点击一下按钮, 按钮变成 5秒后可以重新执行, 在着 5秒内点击这个 按钮, 不能重复触发
*/
<button>点击获取验证码</button>
// 0. 获取元素
var btn = document.querySelector('button')
// 0. 准备变量
var flag = true
// 1. 给元素添加点击事件
btn.onclick = function () {
if (flag === false) return
console.log('当前点击事件执行')
var count = 5
btn.innerText = `${count} 秒后可以重新执行`
flag = false
var timeId = setInterval(function () {
count--
btn.innerText = `${count} 秒后可以重新执行`
if (count === 0) {
btn.innerText = '点击获取验证码'
clearInterval(timeId)
flag = true
}
}, 1000)
}
/**
* 第一次点击按钮时
* flag === true, 所以 31 行 if 判断不可能会执行, 函数会正常往下执行
* 执行到 37 行的时候, 会将 变量 flag 赋值 为 false; 然后再 5 秒钟以后重新赋值为 true
*
* 第二次点击按钮时 (与第一次点击完成时间间隔只有 3 秒钟)
* flag === false, 所以 31 行 if 判断条件成立, 执行代码 return, 所以函数就此中断
*
* 第三次点击按钮时 (与第一次点击完成时间间隔有 30 秒钟)
* flag === true, 所以 31 行 if 判断不可能会执行, 函数会正常往下执行
* 执行到 37 行的时候, 会将 变量 flag 赋值为 false; 然后 5 秒钟以后重新赋值为 true
*/