文章目录
- 一、 DOM事件
- 1. 事件监听
- 2. 事件类型
- (1)、鼠标事件
- (2)、焦点事件
- (3)、键盘事件
- (4)、文本事件
- 3. 事件对象
- (1)、获取事件对象
- (2)、事件对象常用属性
- 4. 环境对象 this
- 5. 回调函数
- 二、 DOM事件进阶
- 1. 事件流
- (1)、 捕获阶段
- (2)、 冒泡阶段
- (3)、 阻止冒泡
- (4) 、阻止元素默认行为
- (5) 、解绑事件
- 2. 事件委托
- 3. 其他事件
- (1)、页面加载事件load
- (2)、页面滚动事件scroll
- (3)、页面滚动事件获取位置(scrollTop)
- (4)、页面滚动到指定坐标scrollTo
- 4. 页面尺寸事件
- (1)、获取元素宽高(client)
- (2)、窗口尺寸发生变化时触发的事件resize
- 5. 页面尺寸与位置offset
一、 DOM事件
事件就是系统内发生的事件,比如单击一个按钮;
事件监听就是让程序检测是否有事件发生,并作出响应。
1. 事件监听
语法
:元素对象.addEventListener(‘事件类型’,要执行的函数)
事件监听三要素:
- 事件源:哪个dom元素的事件被触发了
- 事件类型:事件触发方式 click mouseover
- 事件调用的函数:作出什么响应
事件监听版本
(1)、DOM L0: 事件源.on事件 = function(){} (比如btn.onclick = …)
(2)、 DOM L2: 事件源.addEventListener(‘事件类型’,要执行的函数)
区别: on方式会被覆盖,addEventListener可以绑定多次,拥有更多特性
案例:随机点名
需求:点击开始,随机抽取数组中的数并显示出来。点击结束,停止滚动,删除当前抽取的数据。抽到最后一个数据时,两个按钮禁用。
<body>
<div class="box">
<h1>随机点名</h1>
<p>
名字是:<span class="name">这里显示姓名</span>
</p>
<div class="operator">
<button class="start">开始</button>
<button class="end">结束</button>
</div>
</div>
<script>
// 1. 数组,为了方便观察,用数字代替姓名
const nameList = [1,2,3,4]
// 获取元素
const name = document.querySelector('.box .name')
const start = document.querySelector('.start')
const end = document.querySelector('.end')
let timeId = 0
let num = 0
// 3. 事件监听
start.addEventListener('click', function () {
// 如果数组中只有一个值,两个按钮禁用
if (nameList.length === 1) {
start.disabled = true
end.disabled = true
}
timeId = setInterval(function () {
// 滚动姓名
num = Math.floor(Math.random() * nameList.length)
name.innerHTML = nameList[num]
}, 100)
})
// 4. 关闭事件
end.addEventListener('click', function () {
clearInterval(timeId)
// 删除当前抽取的数组
nameList.splice(num, 1)
})
</script>
</body>
2. 事件类型
(1)、鼠标事件
- 鼠标点击 click
- 鼠标经过 mouseenter
- 鼠标离开 mouseleave
(2)、焦点事件
- 获得焦点 focus
- 失去焦点 blur
(3)、键盘事件
- 键盘按下 keydown
- 键盘抬起触发 keyup
(4)、文本事件
- 用户输入事件 input
案例:评论字数统计
思路:①监听用户输入 ②不断获取文本框内的字符长度,文本域.value.length ③ 把获得的数字给下面文本框
<body>
<div class="comment">
<div class="box">
<textarea class="ipt"></textarea>
<p><span class="left-num">200</span>/200字</p>
</div>
<button>发布</button>
</div>
<script>
// 评论字数统计--- value.length
const comment = document.querySelector('.comment textarea')
comment.addEventListener('input', function () {
const left = 200 - comment.value.length
document.querySelector('.box .left-num').innerHTML = left
})
</script>
</body>
3. 事件对象
事件对象也是个对象,包含事件触发的相关信息
(1)、获取事件对象
在事件监听的回调函数中第一个参数就是事件对象,一般命名为event或e
元素.addEventListner('click',function(e){
// e就是事件对象
})
(2)、事件对象常用属性
type:获取当前的事件类型
clientX/clientY:获取光标相对于浏览器可视窗口左上角的位置(可应用于商品的放大镜场景中,通过获取鼠标的位置来确定要放大的位置)
offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置
key:用户按下的键盘键的值。现在不提倡使用keyCode
const input = document.querySelector('input')
input.addEventListener('keyup', function (e) {
if (e.key === 'Enter') {
console.log('我按下了回车键');
}
})
4. 环境对象 this
环境对象就是函数内部的this,代表当前函数运行时所处的环境
this指向函数的调用者。谁调用函数,this就是谁
function fn () {
console.log(this); // window
}
// 本质上是window调用的fn
fn() // 等价于 window.fn()
// button
const btn = document.querySelector('button');
btn.addEventListener('click', function () {
console.log(this); //this指的是btn对象
this.style.color = 'red'
})
5. 回调函数
就是把函数A当作参数传递给另一个函数B时,函数A叫做回调函数。
使用匿名函数作为回调函数比较常见.
// 函数A作为参数传递给函数B时,函数A为回调函数
// 使用场景1
function fn () {
console.log('我是回调函数');
}
setInterval(fn, 1000)
// 使用场景2
btn.addEventListener('click', function () {
console.log('我也是回调函数');
})
二、 DOM事件进阶
1. 事件流
事件流就是事件执行的整个流动路径;假设页面中有个button,当触发事件时,会经历两个阶段:
(1) 捕获阶段:从父到子(button)
(2) 冒泡阶段:从子(button)到父
(1)、 捕获阶段
从DOM根元素开始执行对应的事件
语法:DOM元素.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
- 第三个参数传入true代表是捕获阶段触发事件,传入false代表冒泡阶段触发事件
- 若是L0监听事件(onclick),则只有冒泡阶段,没有捕获
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
//点击son标签,依次弹出:grandpa,father,son
// document.addEventListener 给最大的对象document添加监听事件
document.addEventListener('click', function () {
alert('grandpa')
}, true)
// father标签监听事件
document.querySelector('.father').addEventListener('click', function () {
alert('father')
}, true)
// son标签监听事件
document.querySelector('.son').addEventListener('click', function () {
alert('son')
}, true)
</script>
页面中的body也有经历了捕获,但是没写对应代码所以没看到效果。事件捕获需要写对应代码才能看到效果
(2)、 冒泡阶段
冒泡:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
;
同名事件:做一次点击,则冒泡会执行每个父级的点击事件(而不是鼠标经过等其他事件),只不过就看该父级有没有添加事件监听。若未添加则看不到任何效果。
事件的触发默认是在冒泡阶段。
// 点击son,依次弹出son,father,grandpa
document.addEventListener('click', function () {
alert('grandpa')
})
document.querySelector('.father').addEventListener('click', function () {
alert('father')
})
document.querySelector('.son').addEventListener('click', function () {
alert('son')
})
(3)、 阻止冒泡
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素。若想把事件就限制在当前元素内,就需要阻止事件冒泡。
语法:事件对象e.stopPropagation()
//阻止冒泡,需要用到事件对象--
document.addEventListener('click', function () {
alert('biggest')
})
document.querySelector('.big').addEventListener('click', function () {
alert('big')
})
document.querySelector('.small').addEventListener('click', function (e) {
alert('small')
e.stopPropagation()
})
注意:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
(4) 、阻止元素默认行为
阻止默认行为的发生,比如阻止链接跳转,表单域跳转;
语法:事件对象e.preventDefault()
<!-- 点击链接不跳转 -->
<a href="https://www.baidu.com">百度</a>
<script>
const a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.preventDefault()
})
</script>
(5) 、解绑事件
const box = document.querySelector('.box')
// 传统L0注册
btn.onclick = function () {
alert('点击')
}
// 解绑事件
btn.onclick = null
// 事件监听注册 L2
function fn () {
console.log('点击事件');
}
box.addEventListener('click', fn)
// 解绑
box.removeEventListener('click', fn)
如果回调函数是匿名函数,则无法解绑
2. 事件委托
比如,快递小哥把一个班的快递委托给班主任,然后学生都找班主任领快递。这样就不用一个一个的找同学去送快递
事件委托是利用事件流的特征解决一些开发需求(只是一种技巧)
- 优点:减少注册次数,可以提高程序性能
- 原理:事件委托其实是利用事件冒泡的特点。给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件。
通过事件对象.target.tagName
可以获得真正触发事件的元素
<!-- 需求:点击哪个li标签,哪个li变色 -->
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
<li>four</li>
<li>five</li>
<p>p标签</p>
</ul>
<script>
// 点击哪个小li,哪个就变色
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
console.dir(e.target);
// 目标元素是li标签,如果是p标签,则颜色不变
if (e.target.tagName === 'LI') {
// 设置颜色!!!!!!
e.target.style.color = 'red'
}
})
</script>
3. 其他事件
之前写的JavaScript都放在HTML文件的底部,因为浏览器会按照代码在文件中的顺序加载HTML。若先加载的JavaScript想要修改下方的HTML,则会因为HTML尚未被加载而失效。
(1)、页面加载事件load
页面加载事件是加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件。
load
:监听页面所有资源加载完毕
// 1.页面加载事件
window.addEventListener('load', function () {
// 执行的操作
})
// 2.也可针对某个资源绑定load事件
img.addEventListener('load',function(){
// 等图片加载完,再执行的操作
})
DOMContentLoaded
事件:DOM元素加载完,无需等待样式表、图像等完全加载
document.addEventListener('DOMContentLoaded', function () {
// 比load触发的快
})
应用场景:图片还没加载完,但是不影响页面进行交互
(2)、页面滚动事件scroll
滚动条在滚动的时候持续触发的事件;
应用场景:固定导航栏,返回顶部…
- 监听整个页面滚动:给
window
或document
添加scroll事件
// 页面滚动事件
window.addEventListener('scroll',function(){
// 执行的操作
})
- 监听某个元素的内部滚动,直接给某个元素加scroll即可
(3)、页面滚动事件获取位置(scrollTop)
scrollLeft
与scrollTop
:获取元素内容往左、往上滚出去看不到的距离;
这两个值是可读写的,即可读值也可赋值。一般在scroll
事件里获取被卷去的距离
案例一:获取div内容滚动的距离
<style>
.box {
overflow: scroll;
width: 400px;
height: 150px;
border: 1px solid black;
margin: 200px;
display: block;
}
</style>
<body>
<div class="box">
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
<p>滚动条在滚动的时候持续触发的事件</p>
</div>
<script>
//div滚动获取
const div = document.querySelector('div')
div.addEventListener('scroll', function () {
console.log(this.scrollTop);
})
</script>
案例二:获取页面滚动的距离
页面滚动的距离主要是看HTML滚动的距离;
获取HTML元素对象:document.documentElement
<style>
body {
height: 3000px;
}
</style>
<body>
<script>
// 获取html:document.documentElement
window.addEventListener('scroll', function () {
// 这句话必须写在里面,因为要时刻获取最新的值
const n = document.documentElement.scrollTop;
console.log(n);
})
</script>
(4)、页面滚动到指定坐标scrollTo
语法:元素.scrollTo(x,y)
作用:把内容滚动到指定的坐标
4. 页面尺寸事件
(1)、获取元素宽高(client)
获取元素的可见部分宽高:clientWidth
和clientHeight
(不包含边框、margin、滚动条等)。如果元素是个块级元素,clientWidth = width +padding
;如果元素是个行内元素,clientWidth = 行内元素被内容撑开的宽度+padding
<style>
.box {
width: 300px;
height: 300px;
padding: 10px;
border: 10px solid black;
background-color: skyblue;
}
</style>
<body>
<div class="box">1212121222</div>
<script>
const box = document.querySelector('.box');
console.log(box.clientWidth); // 320 width+padding,不包含border的宽度
</script>
</body>
(2)、窗口尺寸发生变化时触发的事件resize
window.addEventListener('resize', function () {
// 页面尺寸发生变化时,触发的事件
// 检测html可视区宽度变化
const n = document.documentElement.clientWidth
console.log(n);
})
5. 页面尺寸与位置offset
通过JS的方式,得到元素在页面中的位置;这样当页面滚动到这个位置时,可进行一些操作,就不用自己计算了
- 获取元素自身宽高:offsetWidth与offsetHeight
- 获取出来的是数值,方便计算
- offsetWidth与offsetHeight是自身的宽高+padding+border
- 获取的是可视宽高,如果盒子是隐藏的,获取的结果是0
- 获取位置:
offsetLeft
与offsetTop
- 获取元素距离自己有定位父级元素的左、上距离;
- offsetLeft与offsetTop是只读属性
<style>
.box {
width: 100px;
height: 100px;
background: skyblue;
margin-left: 100px;
}
p {
width: 50px;
height: 50px;
margin-left: 10px;
background: pink;
}
</style>
</head>
<body>
<div class="box">
<p></p>
</div>
<script>
const box = document.querySelector('.box')
console.log(box.offsetLeft); // 108 = 100 + 8(body的8px外边距)
const p = document.querySelector('p')
console.log(p.offsetLeft);
</script>
</body>
若父级无定位,则向上找有定位的父级,值为距自己有定位的父级的距离
若给div加一个相对定位,position:relative
,则p.offsetLeft = 10
;
页面滚动时,offset的值也不变