一、事件流
1.1 事件流的两个阶段
事件流是事件完整执行过程中的流动路径
捕获阶段:
Document ---> Element html ---> Element body ---> Element div
冒泡阶段:
Element div ---> Element body ---> Element html ---> Document
1.2 事件捕获
从DOM的根元素执行事件(从外到里)
1.3 事件冒泡
当一个元素的事件被触发时,同样的事件会在该元素的所有祖先元素中依次被触发(从里到外)
只会触发所有父元素的同名事件
//第三个参数默认false -冒泡 如果是true -捕获
DOM对象.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
1.4 阻止冒泡
默认有冒泡模式存在,事件容易影响到父级元素
//冒泡和捕获都可以阻止
事件对象.stopPropagation()
<!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>
.father {
width: 500px;
height: 500px;
background-color: pink;
}
.son {
width: 200px;
height: 200px;
background-color: purple;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
<script>
const fa = document.querySelector('.father')
const son = document.querySelector('.son')
// 山东 济南 蓝翔 目标(pink老师) 捕获阶段
// 蓝翔 济南 山东 冒泡阶段
document.addEventListener('click', function () {
alert('我是爷爷')
})
fa.addEventListener('click', function () {
alert('我是爸爸')
})
son.addEventListener('click', function (e) {
alert('我是儿子')
// 组织流动传播 事件对象.stopPropagation()
e.stopPropagation()
})
</script>
</body>
</html>
阻止元素默认行为
<a href="http://www.baidu.com">百度一下</a>
<script>
const a = document.querySelector('a')
a.addEventListener('click',function(e){
//阻止a标签的默认跳转行为
e.preventDefault()
})
</script>
1.5 解绑事件
匿名函数无法被解绑
const btn = document.querySelector('button')
function fn(){
alert('点击了')
}
btn.addEventListener('click', fn)
//解绑
btn.removeEventListener('click', fn)
1.6 鼠标经过事件
mouseover和mouseout有冒泡效果
mouseenter和mouseleave没有冒泡效果 (建议使用)
二、事件委托
同时给多个元素注册事件--利用事件冒泡
给父元素注册事件,当触发子元素时,会冒泡到父元素上,触发父元素的事件
//点击一个li所有的li都会变红
const ul = document.querySelector('ul')
ul.addEventListener('click',function(){
ul.style.color = 'red'
})
//点击的对象变色
const ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
e.target.style.color = 'red'
})
//指定事件响应的特定元素
const ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
if(e.target.tagName === 'LI'){
e.target.style.color = 'red'
}
})
tab栏案例优化(去掉循环)
<!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>tab栏切换</title>
<style>
* {
margin: 0;
padding: 0;
}
.tab {
width: 590px;
height: 340px;
margin: 20px;
border: 1px solid #e4e4e4;
}
.tab-nav {
width: 100%;
height: 60px;
line-height: 60px;
display: flex;
justify-content: space-between;
}
.tab-nav h3 {
font-size: 24px;
font-weight: normal;
margin-left: 20px;
}
.tab-nav ul {
list-style: none;
display: flex;
justify-content: flex-end;
}
.tab-nav ul li {
margin: 0 20px;
font-size: 14px;
}
.tab-nav ul li a {
text-decoration: none;
border-bottom: 2px solid transparent;
color: #333;
}
.tab-nav ul li a.active {
border-color: #e1251b;
color: #e1251b;
}
.tab-content {
padding: 0 16px;
}
.tab-content .item {
display: none;
}
.tab-content .item.active {
display: block;
}
</style>
</head>
<body>
<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"><img src="./image/tab00.png" alt="" /></div>
<div class="item"><img src="./image/tab01.png" alt="" /></div>
<div class="item"><img src="./image/tab02.png" alt="" /></div>
<div class="item"><img src="./image/tab03.png" alt="" /></div>
<div class="item"><img src="./image/tab04.png" alt="" /></div>
</div>
</div>
<script>
const ul = document.querySelector('.tab-nav ul')
ul.addEventListener('click', function(e){
//mouseenter没有e.target对象
if(e.target.tagName === 'A'){
document.querySelector('.tab-nav .active').classList.remove('active')
console.log(e.target)
e.target.classList.add('active')
//获取自定义属性
const id = e.target.dataset.id
document.querySelector('.tab-content .active').classList.remove('active')
//id过来是字符串 要转化成数字
document.querySelector(`.tab-content .item:nth-child(${+id+1})`).classList.add('active')
}
})
</script>
</body>
</html>
三、其他事件
3.1 页面加载事件
外部资源加载完毕时触发的事件
//等待页面所有资源加载完毕 回去执行回调函数
//如果script标签写在head中
window.addEventListener('load',function(e){
})
//当html文档加载完之后 事件可以被触发
//比load更快
document.addEventListener('DOMContentLoaded',function(){
})
3.2 页面滚动事件
滚动条再滚动的时候持续触发的事件
应用:固定导航栏/返回顶部
window.addEventListener('scroll',function(e){
console.log('我滚了');
})
scrollTop和scrollLeft
被卷去的内容高度/内容宽度
<!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>
body{
height: 3000px;
}
div{
opacity: 0;
margin: 500px auto;
height: 200px;
width: 200px;
background-color: pink;
transition: all 0.5s;
}
</style>
</head>
<body>
<div></div>
<script>
window.addEventListener('scroll',function(e){
//document.documentElement html元素的获取方式
const n = document.documentElement.scrollTop
const div = document.querySelector('div')
if( n >= 200){
div.style.opacity = '1'
}
})
</script>
</body>
</html>
3.3 页面尺寸事件
//窗口尺寸改变时触发
window.addEventListener('resize',function(){
})
获取元素宽高
clientWidth / clientHeight
不包括border、margin、滚动条,包括padding
四、元素尺寸与位置
4.1 获取元素自身的宽高
包括padding、border、滚动条
offsetWidth / offsetHeight
获取到的是[可视宽高]
4.2 获取元素位置
元素距离自己定位父级元素的左、上距离(如果没有定位,就一直往上找到有定位的父元素)
offsetLeft / offsetTop 只读属性
4.3 获取元素相对于视口的位置
element.getBoundingClientRect()