事件流
事件完整执行过程中的流动路径,事件流的两个阶段:捕获阶段和冒泡阶段
捕获阶段
从大到小 从父到子
//第三个参数传入true代表捕获阶段触发,很少使用
//第三个参数默认为false,false代表冒泡阶段触发
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
<div class="father">
<div class="son"></div>
</div>
<script>
//捕获阶段 从大到小依次展示
document.addEventListener('click',function(){
alert('我是爷爷')
},true)
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
fa.addEventListener('click',function(){
alert('我是爸爸')
},true)
son.addEventListener('click',function(){
alert('我是儿子')
},true)
</script>
冒泡阶段
从小到大 从子到父 (实际工作中使用事件冒泡为主)
当元素触发事件后,同名事件会从小到大不断向上调用所有祖先元素
事件冒泡默认存在,第三个参数默认为false,false是事件冒泡
<div class="father">
<div class="son"></div>
</div>
<script>
//冒泡阶段,从小到大依次展示
document.addEventListener('click',function(){
alert('我是爷爷')
})
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
fa.addEventListener('click',function(){
alert('我是爸爸')
})
son.addEventListener('click',function(){
alert('我是儿子')
})
</script>
阻止事件冒泡
由于默认就有冒泡模式的存在,容易导致事件影响父级元素,需阻断事件流动传播
事件对象.stopPropagation()
<div class="father">
<div class="son"></div>
</div>
<script>
document.addEventListener('click',function(){
alert('我是爷爷')
})
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
fa.addEventListener('click',function(){
alert('我是爸爸')
})
son.addEventListener('click',function(e){
alert('我是儿子')
//阻断事件冒泡
e.stopPropagation()
})
</script>
阻止默认行为
e.preventDefault()
<a href="http://www.baidu.com" >百度一下</a>
<script>
const a= document.querySelector('a')
a.addEventListener('click',function(e){
e.preventDefault()
})
</script>
解绑事件
1.on事件方式,直接用null覆盖
//绑定事件
btn.onclick = function(){
alert('点击一次')
}
//解绑事件
btn.onclick = null
2.addEventListener监听事件方式,用removeEventListener
function fn(){
alert('点击一次')
}
btn.addEventListener('click',fn)
//匿名函数无法被解绑
btn.removeEventListener('click',fn)
事件委托
利用事件冒泡特点,给父元素监听事件,当我们触发子元素时,会向上冒泡查找到父元素,能减少事件监听次数,提高程序性能
找到真正触发的元素 事件对象.target.tagName
<ul>
<li class="active">第1个孩子</li>
<li>第2个孩子</li>
<li>第3个孩子</li>
<li>第4个孩子</li>
<li>第5个孩子</li>
<p>我是不</p>
</ul>
<script>
const ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
//事件委托
if(e.target.tagName === 'LI'){
document.querySelector('ul .active').classList.remove('active')
e.target.classList.add('active')
}
})
</script>
案例 -- tab切换栏进阶版
<div class="tab">
<div class="tab-nav">
<h3>每日特价</h3>
<ul>
<li><a class="active" href="javascript:;" data-id="0">精选</a></li>
<li><a 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>
</ul>
</div>
<div class="tab-content">
<div class="item active">1<img src="./images/tab00.png" alt="" /></div>
<div class="item">2<img src="./images/tab01.png" alt="" /></div>
<div class="item">3<img src="./images/tab02.png" alt="" /></div>
<div class="item">4<img src="./images/tab03.png" alt="" /></div>
<div class="item">5<img src="./images/tab04.png" alt="" /></div>
</div>
</div>
<script>
const ul = document.querySelector('.tab-nav ul')
ul.addEventListener('click',function(e){
//事件委托 判断是否是a标签
if(e.target.tagName === 'A'){
document.querySelector('.tab-nav ul li .active').classList.remove('active')
//当前对象
e.target.classList.add('active')
//自定义标签属性 dataset
const i = +e.target.dataset.id
document.querySelector('.tab-content .active').classList.remove('active')
const item = document.querySelectorAll('.tab-content .item')
item[i].classList.add('active')
}
})
</script>
页面加载事件
load:监听页面中所有资源css,html,js全部加载完毕,给window添加load事件
//写在html头部head标签内
window.addEventListener('load',function(){
//所有资源加载完毕执行该操作
const btn = document.querySelector('button')
btn.addEventListener('click',function(){
alert('111')
})
})
DOMContentLoaded,给document添加,无需等待样式表和图标完全加载
//写在html头部head标签内
document.addEventListener('DOMContentLoaded',function(){
//const btn = document.querySelector('button')
//btn.addEventListener('click',function(){
//alert('111')
})
})
页面滚动事件
scroll:监听整个页面的滚动,如固定导航栏,返回顶底部
scrollLeft和scrollTop属性
//检测页面的往上滚动距离
window.addEventListener('scroll',function(){
// 获取html元素的写法 document.documentElement
const n = document.documentElement.scrollTop
//返回为数字型
console.log(n);
})
const elevator = document.querySelector('.elevator')
const entry = document.querySelector('.nav-bar')
window.addEventListener('scroll',function(){
// 获取html元素的写法 document.documentElement
const n = document.documentElement.scrollTop
//三元判断 向上滚动距离 元素显示或隐藏
elevator.style.opacity = n >= entry.offsetTop ? 0 : 1
//点击按钮返回页面顶部
const backTop = document.querySelector('.backTop')
backTop.addEventListener('click',function(){
document.documentElement.scrollTop = 0
})
})
})
页面尺寸事件
窗口尺寸改变时触发事件
//resize浏览器窗口大小发生变化时触发
window.addEventListener('resize',function(){
console.log('11');
})
获取元素的可见部分宽高(不包括border,margin,滚动条等,除了包括padding)
clientWidth和clientHeight
//浏览器窗口变化时获取页面的宽度和高度
window.addEventListener('resize',function(){
//console.log(document.documentElement.clientWidth)
console.log(document.documentElement.clientHeight)
})
元素尺寸与位置
尺寸:获取元素自身的宽高,包含元素自身设置的宽高,padding,border
offsetWidth和offsetHeight
位置:获取元素位于自己定位父级元素的左,上距离
offsetTop和offsetLeft
const div = document.querySelector('div')
console.log(div.offsetTop);
2.位置:返回元素的大小,相对于可视区域的位置
getBoundingClientRect()
案例 -- 仿顶部导航固定栏显示隐藏
<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.content {
overflow: hidden;
width: 1000px;
height: 3000px;
background-color: pink;
margin: 0 auto;
}
.backtop {
display: none;
width: 50px;
left: 50%;
margin: 0 0 0 505px;
position: fixed;
bottom: 60px;
z-index: 100;
}
.backtop a {
height: 50px;
width: 50px;
background: url(./images/bg2.png) 0 -600px no-repeat;
opacity: 0.35;
overflow: hidden;
display: block;
text-indent: -999em;
cursor: pointer;
}
.header {
position: fixed;
top: -80px;
left: 0;
width: 100%;
height: 80px;
background-color: purple;
text-align: center;
color: #fff;
line-height: 80px;
font-size: 30px;
transition: all .3s;
}
.sk {
width: 300px;
height: 300px;
background-color: skyblue;
margin-top: 500px;
}
</style>
</head>
<body>
<div class="header">我是顶部导航栏</div>
<div class="content">
<div class="sk">秒杀模块</div>
</div>
<div class="backtop">
<img src="./images/close2.png" alt="">
<a href="javascript:;"></a>
</div>
<script>
const sk = document.querySelector('.sk')
const head = document.querySelector('.header')
window.addEventListener('scroll',function(){
const n = document.documentElement.scrollTop
// if (n >= sk.offsetTop) {
// header.style.top = 0
// } else {
// header.style.top = '-80px'
// }
//css一定要跟单位
head.style.top = n >= sk.offsetTop ? 0 : '-80px'
})
</script>
</body>
</html>
案例 -- 仿b站导航tab栏下方滑块切换
<style>
.tabs-list {
display: flex;
}
.tabs-list a {
padding: 0 4.26666667vw;
white-space: nowrap;
font-size: 3.73333333vw;
height: 10.66666667vw;
line-height: 10.66666667vw;
}
.tabs-list .line {
position: absolute;
left: 4.26666667vw;
bottom: 0;
width: 7.46666667vw;
height: 0.53333333vw;
background-color: #fb7299;
transition: all 0.3s;
}
</style>
<div class="tabs-list">
<a href="#">首页</a>
<a href="#">动画</a>
<a href="#">番剧</a>
<a href="#">果蔬</a>
<a href="#">音乐</a>
<a href="#">舞蹈</a>
<a href="#">鬼畜</a>
<a href="#">吹鬼</a>
<!-- 红色线 -->
<div class="line"></div>
<script>
const list = document.querySelector('.tabs-list')
const line = document.querySelector('.line')
//事件委托
list.addEventListener('click',function(e){
//判断点击是否是a标签
if(e.target.tagName === 'A'){
//滑块移动距离是当前a标签到最左边的距离
line.style.transform = `translateX(${e.target.offsetLeft}px)`
}
})
</script>