网上主流解释
:函数防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。笔者解释
:一句话总结
:用户连续多次触发某个事件,则只执行最后一次网页性能优化
技术,因此初学者刚开始学习会有一些吃力,并且很多网站都没有做防抖处理(性能优化)没有做函数防抖处理的用户体验如下
第一张
,此时用户想看第五张
。正常情况下,鼠标会依次触发 第二、第三、第四张的移入事件,但这不是用户真正想要触发的(误触发)有做函数防抖处理的用户体验如下
第一张
滑动到第五张
,由于鼠标在 第二、第三、第四张停留时间很短(假设小于0.5秒),所以判定为用户误触发,则不触发对应的事件处理函数注意点
:定时器中的this默认为window,需要使用上下文模式bind()
动态修改为当前事件源<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>动画-案例《手风琴》</title>
<style>
* {
margin: 0;
padding: 0;
}
ul {
list-style: none;
width: 2400px;
}
#box {
width: 1200px;
height: 400px;
border: 2px solid red;
margin: 100px auto;
overflow: hidden;
}
#box li {
width: 240px;
height: 400px;
float: left;
}
</style>
</head>
<body>
<div id="box">
<ul>
<li><img src="./images/collapse/1.jpg" alt=""></li>
<li><img src="./images/collapse/2.jpg" alt=""></li>
<li><img src="./images/collapse/3.jpg" alt=""></li>
<li><img src="./images/collapse/4.jpg" alt=""></li>
<li><img src="./images/collapse/5.jpg" alt=""></li>
</ul>
</div>
<script src="./animation.js"></script>
<script>
/*
1. 鼠标抖动 : 例如用户鼠标轻微晃动,快速划过某一个元素(用户本身不想触发,只是鼠标误触发,常见于鼠标事件 移入/移出/移动 )
2. 函数防抖 : 用户连续多次触发某个事件,则只执行最后一次
* 如果用户鼠标轻微晃动,在某一个元素上停留时间很短,则认为是用户误触发,则不执行本次事件处理函数
* 影响 : 会触发一些本没有必要触发的事件,浪费代码性能
3. 函数防抖解决思路 :
3.1 声明全局变量存储定时器ID
3.2 每一次移入元素时 : 先清除上一次的定时器
* 以最后一次触发为准
3.3 开启定时器,间隔时间才会触发事件处理函数
* 用户连续触发事件的时候,全局变量只会存储最后一次触发的事件定时器(前面的都被清除了,认为这是用户误操作产生抖动)
*/
/*需求
(1):给每一个li设置鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
(2):鼠标移出大盒子,所有的li的宽度都变成默认的240
*/
//1.获取元素
var liList = document.querySelectorAll('#box li');//li元素列表
var box = document.querySelector('#box');//li元素列表
//2.注册事件
// 函数防抖一 : 声明全局变量存储定时器
var timeID = null;
//2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
for (var i = 0; i < liList.length; i++) {
liList[i].onmouseover = function () {
//this:事件源
// 函数防抖二 : 每一次移动新的li元素,移除旧的定时器,以最后一次触发为准
clearTimeout(timeID);
//函数防抖三 : 间隔500ms后执行
/* 注意点:定时器中的this默认是window,需要使用bind修改为事件源 */
timeID = setTimeout(function () {
//3.事件处理函数 (触发事件)
console.log(1111);
//3.排他思想修改样式
for (var j = 0; j < liList.length; j++) {
if (liList[j] == this) {//是自己
animationSlow(liList[j], { width: 800 });
} else {
animationSlow(liList[j], { width: 100 });
}
};
}.bind(this), 500);
};
};
//2.2 鼠标移出大盒子,所有的li的宽度都变成默认的240
/* 这个事件目前对我们的函数防抖会有影响 : 事件冒泡 */
/* jq第一天就会讲onmouseout对事件的影响 */
// box.onmouseout = function(){
// for(var i = 0;i<liList.length;i++){
// animationSlow( liList[i] , {width:240});
// };
// };
</script>
</body>
</html>
<script>
//1.获取元素
var liList = document.querySelectorAll('#box li');//li元素列表
var box = document.querySelector('#box');//li元素列表
//2.1 鼠标移入事件:当前li的宽度变成800,其他兄弟li宽度变成100
for (var i = 0; i < liList.length; i++) {
liList[i].onmouseover = function () {
//this:事件源
//调用防抖函数, 防抖间隔为500ms
debonce(function () {
//3.事件处理函数 (触发事件)
console.log(1111);
//3.排他思想修改样式
for (var j = 0; j < liList.length; j++) {
if (liList[j] == this) {//是自己
animationSlow(liList[j], { width: 800 });
} else {
animationSlow(liList[j], { width: 100 });
}
};
}.bind(this), 500);
};
};
/**
* @description: 万能防抖函数
* @param {type} fn : 事件处理函数
* @param {type} timeout : 防抖时间间隔
* @return:
*/
function debonce(fn, timeout) {
/* 核心技术点
1.函数防抖需要使用定时器id, 这个id不能是局部的(函数走完之后会被回收)
2.定时器id也不能是全局的,造成全局变量污染
3.解决方案:
(1)使用闭包延长局部变量生命周期,但是闭包语法很繁琐
(2)利用函数本身也是对象,使用函数本身的静态成员来存储定时器ID
*/
//1.先清除上一次触发事件 的定时器
clearTimeout(debonce.timeID);
//2.以最后一次触发 为准
debonce.timeID = setTimeout(fn, timeout);
};
</script>
高频事件
高频事件
: 触发频率极高的事件。例如 滚动条事件onscroll
鼠标移动onmousemove
网页大小变化onresize等
高频触发
:事件本身不是高频的,但是用户可以通过很快的手速来触发。例如用户疯狂快速点击 抢购按钮(onclick,onmousedown)浪费资源,降低网页速度
,甚至导致浏览器卡死:由于高频事件触发非常频繁,可能1秒钟会执行几十次甚至上百次,如果在这些函数内部,又调用了其他函数,尤其是操作了DOM (DOM操作耗性能且可能导致浏览器出现回流) ,不仅仅会降低整个网页的运行速度,甚至会造成浏览器卡死,崩溃。网络堵塞
: 如果在高频事件中,进行了重复的ajax请求,可能会导致请求数据出现混乱,并且还占用服务器带宽增加服务器压力 (间接增加服务器成本)==限制事件处理函数的执行周期==
节流间隔
//函数节流1 : 声明变量记录上一次事件触发时间
var lastTime = 0;
//1.1 鼠标移动事件
var i = 0;
window.onmousemove = function () {
//函数节流2 : 判断触发时间间隔 (0.5秒 = 500ms)
var currentTime = new Date().getTime(); //当前时间
if (currentTime - lastTime > 500) {
//事件处理函数
i++;
console.log('鼠标移动事件被触发' + i + '次');
//函数节流3 : 事件成功触发之后,当前时间 成为 下一次触发间隔参考时间
//说人话:现任女友已经结束了, 她已经成为下一任女友 故事里的 前任女友
lastTime = currentTime;
};
};、
节流函数的事件处理
都是不一样的,他们可能是鼠标移入、鼠标移出、鼠标移动。 每一个案例需要的节流间隔
也不同/**
* @description: 函数节流
* @param {type} fn: 事件处理函数
* @param {type} timeout: 节流间隔
* @return:
*/
function throttle(fn, timeout) {
/*核心技术介绍
1. 函数节流需要使用变量来存储 上一次触发时间
2. 这个变量如果是局部变量 : 则函数完毕会被回收。 如果是全局变量:则会造成全局变量污染
3.解决方案 : 利用函数本身也是对象,使用函数本身的静态成员来存储 上一次触发时间
*/
//给throttle添加静态成员lastTime
if(!throttle.lastTime){
/* 为什么一定要有这一步呢?
因为函数对象的属性不存在时,默认取值会得到undefined,而undefined在做数学计算
的时候会转成number类型得到NaN. Number(undefined) 结果是NaN。无法计算
*/
throttle.lastTime = 0;
};
//1.记录当前时间
var currentTime = new Date().getTime();
//2.判断触发时间间隔
if (currentTime - throttle.lastTime > timeout) {
fn();
//3.将当前时间作为 下一次触发时间 参考时间
throttle.lastTime = currentTime;
};
};
//1.2 滚动条事件
var j = 0;
window.onscroll = function () {
//节流函数 : 滚动条 0.5s 只会执行一次
throttle(function () {
j++;
console.log('滚动事件被触发' + j + '次');
}, 500);
};
//2. 高频触发 :事件本身触发不频繁, 但是用户可以通过疯狂触发来实现高频
var btn = document.querySelector('#btn');
var p1 = document.querySelector('#p1');
var k = 0;
btn.onclick = function () {
//节流函数 : 按钮1s 只能点击一次
throttle(function () {
k++;
p1.innerText = '点击事件被触发' + k + '次';
}, 1000);
};
1.函数防抖:用户多次触发事件,以最后一次触发为准
2.函数节流:限制事件的执行周期(500ms内指挥执行一次)
3函数防抖与函数节流的异同点 与应用场景
(1)相同点:都是为了优化js代码执行频率,提高代码的性能
(2)不同点:
函数防抖
:由用户需求决定
a.鼠标移入移出,用户快速移动鼠标,应该等用户结束后,以最后一次为主
b.输入框事件:验证手机号或者邮箱,用户输入时不断触发键盘事件,应该等用户结束输入之后,以最后一次输入为准
函数节流
:由事件本身决定 (高频事件)
a. onscroll
: 高频事件(滚动条事件)
b. onmousemove
:高频事件(鼠标移动)
* 特殊情况:高频触发 (抢购按钮点击,本身不是高频事件,用户可以用手速实现点击高频)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。