事件链的原理
事件链的原理其实就是事件的响应
事件响应顺序就是先事件捕获在事件冒泡
事件捕获:document —>html —>body—>div(目标)
事件冒泡: div(目标)—>body – >html —>document
阻止事件冒泡
//addEventListener第三个参数 true表示捕获阶段触发 false表示冒泡阶段触发
var box1=document.querySelector(".box1")
var box2=document.querySelector(".box2")
var box3=document.querySelector(".box3")
box1.addEventListener("click",(e)=>{
console.log("box1",e)
},true)
box2.addEventListener("click",(e)=>{
console.log("box2")
})
box3.addEventListener("click",(e)=>{
// 阻止事件冒泡
e.stopPropagation()
console.log("box3")
})
那么现在有一个问题:addEventListener的第三个参数为true是阻止事件传递还是false?
答案是都不会阻止事件传递,因为true捕获阶段触发 false冒泡阶段触发
要阻止事件传递唯一的方式就是阻止事件冒泡:事件对象调用stopPropagation()
代码还可以优化一下
下面是代码的兼容性写法
box1.addEventListener("click",(e)=>{
console.log("box2a",e)
})
box3.addEventListener("click",(e)=>{
console.log("box3a",e)
//智能浏览器:
//阻止向父级元素冒泡
e.stopPropagation()
//阻止程序传递执行冒泡
e.stopImmediatePropagation()
//ie8以下
e.cancelBubble=false
})
box3.addEventListener("click",(e)=>{
console.log("box3b",e)
})
注意:阻止冒泡,设计让监听器在捕获阶段运行等等 都是指的同一个事件,不同事件上来绑定是没有用的,因为事件之间的运行时独立的互不影响的
阻止系统默认事件
a1.addEventListener("click",(e)=>{
console.log("666")
//阻止系统默认事件
//可以阻止默认事件
e.preventDefault()
//阻止不了默认的跳转页面的系统事件
e.stopPropagation()
//阻止不了默认的跳转页面的系统事件
e.stopImmediatePropagation()
})
事件代理
事件代理:网页设计的中一种设计思想 利用事件对象中引用的目标对象这个技术实现的无论事件触发时是不是目标对象的监听器 在监听器内部的事件对象event中都可以访问这个事件的目标对象,利用这个特点去绑定事件给父级元素 来代理子集元素的业务,这种设计就是事件代理
为什么要用事件代理呢?
因为使用原始的事件绑定会有两个缺点:
- 静态的事件绑定:如果动态的添加元素进去 添加进去的元素没有这个事件
- 性能消耗更高 业务却相同
而事件代理就可以解决这样的缺点
<style>
.box1{
background-color: darkgray;
}
.box2{
width: 120px;
height: 40px;
background-color: firebrick;
margin: 10px;
}
</style>
<div class="box1">
<div class="box2">hello1</div>
<div class="box2">hello2</div>
<div class="box2">hello3</div>
<div class="box2">hello4</div>
</div>
<button onclick="load1()">网络请求了新的新闻</button>
<script>
function load1(){
var box1=document.querySelector(".box1")
box1.innerHTML+='<div class="box2">hello5</div>'
var box1=document.querySelector(".box1")
var box2=document.createElement("div")
box2.innerHTML="hello5"
box2.className="box2"
box1.appendChild(box2)
}
// 使用事件代理,在父元素上绑定事件,子元素点击一样可以实现事件的响应,不管点击那一个子元素,都会在控制点打印系响应的内容
var box1=document.querySelector(".box1")
box1.addEventListener("click",function(e){
console.log(e.target.innerHTML)
})
</script>
页面的渲染流程
浏览器加载一份html文档是怎么加载的?
- 把标签 文本 注释 属性等等 解析为节点树(DOM Tree)
- 解析DOMtree中的节点时遇到了不同的元素会有不同的操作:
- style标签或者link-css 遇到css代码 就会把css代码解析为CSS样式结构体
- 遇到了src 会去加载(网络请求)资源
- 把2.1CSS样式结构体和1DOM Tree结合变成呈现树/渲染树(Render Tree)
- 按照Render Tree绘制页面
重绘 和 回流/回档
/重绘 就是按照文档树的结构 重新绘制页面 渲染
回流/回档 就是页面的元素排版布局数量和节点在树中位置等发生了改变 就是回流
而且回流必然引起重绘 但是重绘不一定引起回流
频繁的重绘/回流会造成页面性能不好 ==> 页面卡顿 迟缓 手机发烫
解决方案有以下两点:
- 尽量避免高频重绘
- 使用框架(MVVM)
function change1 () {
var box=document.querySelector(".box")
box.innerHTML="6666"//回档
box.style.visibility= "hidden";//重绘不回流
box.style.display= "none";//回流
}
style的操作
可以直接这样去获取内联样式,但是不能这个去获取样式表里面的样式,所以要用window.getComputedStyle() 去获取
<style>
.box {
width: 400px;
height: 300px;
background-color: brown;
cursor: pointer;
}
</style>
<div class='box' style="color: red;font-size: 18px;">666</div>
<script>
var body1=document.body
var box2=document.querySelector(".box2")
var color1=document.querySelector('.box').style.color
var width1=document.querySelector('.box').style.width
console.log(body1,box2,color1)
console.log(width1)
</script>
还有一个需要注意的地方
获取script脚本节点后面加载的元素节点 是获取不了的,因为文档的加载是按照文档树的顺序加载的,可以这样来获取:
- 当页面加载完成的事件触发 再去执行获取节点的方式
- script-- defer async 修饰src如何加载外部js资源的异步属性
获取样式表内的样式,以便来做修改
<div class="box2">
6662
</div>
<button id="btn3">让box发生变化x2</button>
<script>
//点击后 宽高和字体x2
btn3.onclick = function() {
//在这里可以获取后面的元素:这个函数运行时 是用户点击 这时候 页面已经加载完毕 它比window.onload事件更加靠后触发
var box = document.querySelector(".box")
var style1= window.getComputedStyle(box)//获取计算样式(呈现树中的)
box.style.fontSize = parseInt(box.style.fontSize) * 2 + 'px'
// 这个样子获取是 获取不了的
// box.style.width=parseInt(box.style.width)*2+'px'//不会生效
box.style.width = parseInt(window.getComputedStyle(box).width) * 2 + 'px'
}
</script>
获取行内样式的,ie678可以这样获取:box.currentSytle.width
现在的主流浏览器都是这样获取的:box.style.width 现代浏览器
节流:比如:高频触发的业务 需要让它的频率降下来
防抖和节流
对于一些高频率触发的业务l例如: 抽奖 登录 动画 网络加载等等需要做防抖或者是节流操作
防抖比如:单位时间内只运行一次,在单位时间内按下了暂停,在重新运行就是重新再走一个单位时间这种思想就是防抖是思想
<style>
.box {
width: 100px;
height: 100px;
background-color: brown;
cursor: pointer;
position: absolute;
left: 100px;
}
</style>
<div class='box'>666</div>
<script>
// 防抖
document.onclick = fangdou(function(e) {
console.log(6666)
}, 1000)
function fangdou(cb, delay) {
var timer = null;
return function() {
//return这个函数频率很高 想办法让cb()执行慢下来:防抖
clearTimeout(timer)
timer = setTimeout(function() {
cb()
}, delay)
}
}
//节流
document.onmousemove=jieliu(function(){console.log(666)},20)
function jieliu(cb,daley){
var timer=null;
return function(){
if(!timer){
timer=setTimeout(()=>{
cb();
timer=null
},daley)
}
}
}
防抖/节流的代码优化
function jieliu2(cb,daley){
var timer=null;
return function(){
let arg=arguments
if(!timer){
timer=setTimeout(()=>{
cb.apply(this,arg);
timer=null
},daley)
}
}
}
//浏览器的极限绘制频率是60帧 16.7
let shijianhanshu=jieliu2(function(e){
box.style.left=e.pageX-box.offsetWidth/2+"px"
box.style.top=e.pageY-box.offsetHeight/2+"px"
console.log(11111)
},17)
document.addEventListener("mousemove",shijianhanshu)
function fangdou2(cb, delay) {
var timer = null;
return function() {
let arg=arguments
clearTimeout(timer)
timer = setTimeout(()=>{
cb.apply(this,arg)
}, delay)
}
}
</script>
懒加载和预加载
预加载:提前加载资源(页面还没出来就加载资源了)-- 同源加载的优化(就是加载过一次的资源就会存在临时资源的本地文件中,下一次加载就直接从本地文件中调用就不需要浪费资源去加载了)
懒加载:先不加载,等条件成立时再加载
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">
<img src="https://img2.baidu.com/it/u=1814268193,3619863984&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=d974afe62dd1225c4faa14b1538bf7f5">
// 要想看到效果写的内容就要超过界面,这里就不写了,直接写js的代码
<script>
window.onload=function(){
document.onscroll=function(e){
let top=window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop
let h=cHeight =window.innerHeight||document.body.clientHeight;
console.log(top,img2.offsetTop-h-100)
if(top>=(img2.offsetTop-h-100)){
console.log(33333333333)
img2.src=img2.dataset.src1
}
}
}
</script>
<img id="img2" data-src1="https://img1.baidu.com/it/u=1966616150,2146512490&fm=253&app=138&size=w931&n=0&f=JPEG&fmt=auto?sec=1658422800&t=f99225a791b634226dcd5ee47c8b5f3f">