1. 防抖与节流
1.1 定义
在一定时间之内, 只允许触发事件一次, 防止多次触发.
1.2 区别
- 防抖的意义在执行的是用户最后一次触发的事件, 而节流执行的是用户第一次触发的事件.
1.3 场景重现
防抖
: 输入搜索框随时监听用户键盘输入, 及时返回关于搜索的关键字供用户选择, 如何解决用户敲一下键盘我们就调用一次服务器, 频繁调用服务器, 增大服务器的开销的问题?节流
: 前端调用服务端接口, 在接口没有返回的同时, 我们如何防止用户再次点击事件按钮多次执行数据?
1.4 解决思路
防抖
: 我们可以 延迟执行 触发函数, 一旦还没有执行函数用户再次触发事件, 那么我们抛弃上一步的事件执行最新一次的事件. 关键字延迟执行
. 核心代码如下:
// 监听键盘事件模拟搜索场景.
let shakeTimeOut = null;
searchBox.addEventListener("keyup", function () {
let value = searchBox.value.trim();
if (!value) {
return false;
}
// 只要重新输入清空以前的定时任务
if (shakeTimeOut) {
clearTimeout(shakeTimeOut);
}
// 重新定义最新的定时任务
shakeTimeOut = setTimeout(function () {
// 真正执行函数部分
console.debug(value);
}, 1000);
});
节流
我们可以 通过控制一段时间内关键字状态 来执行函数. 核心代码如下:
// 利用 saveStreamFlag 关键字控制代码执行
let saveStreamFlag = true;
searchBtn.addEventListener("click", function () {
if (!saveStreamFlag) {
console.debug("搜索值太频繁");
return false;
}
saveStreamFlag = false;
saveStream();
// 执行 saveStream() 方法的时间不会影响改变 saveStreamFlag标识改变的时间
setTimeout(function () {
saveStreamFlag = true;
// 我们可以预估方法执行的时间来改变标识的时间
}, 1000);
});
1.5 逻辑代码
代码中涉及到了利用H5
创建本地数据库
, 一旦触发搜索, 我们将用户搜索的记录存储在数据库中, 利用input[list] 和datalist
标签实现输入搜索框的实现(代码中涉及背景图片和字体资源请忽略).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Js实现防抖和节流</title>
</head>
<style type="text/css">
* {
margin: 0;
padding: 0;
}
/*定义外部网络字体*/
@font-face {
font-family: WebFont;
src: url("font/ErZiYuanMoFaShaoNvJian-2.ttf");
}
body {
font-family: WebFont;
font-size: 20px;
background: black;
}
#box {
margin: auto;
width: 1280px;
height: 800px;
background-image: url("image/backend.jpg");
background-size: cover;
border: none;
}
#content {
position: fixed;
top: 20%;
left: 50%;
transform: translateX(-50%);
display: table;
text-align: center;
color: #e91e63;
}
#content section {
display: inline-block;
width: 500px;
height: 300px;
}
#preventShakeBox input{
height: 30px;
line-height: 30px;
outline: none;
border-radius: 5px;
border: none;
padding-left: 5px;
}
#preventShakeBox input::placeholder {
color: #999999;
}
#saveStreamBox input{
height: 30px;
line-height: 30px;
outline: none;
border-radius: 5px;
border: none;
padding: 0 20px;
cursor: pointer;
}
#saveStreamBox input:hover {
background-color: #e91e63;
color: #ffffff;
}
</style>
<body>
<div id="box">
<section id="content">
<h1>Js实现防抖与节流</h1>
<section id="preventShakeBox">
<h2>防抖</h2>
<!--利用 input 和 datalist 标签绑定实现可输入和下拉的功能-->
<input type="text" placeholder="请输入要搜索的值" aria-label="防抖" list="data" id="searchBox"/>
<datalist id="data">
</datalist>
</section>
<section id="saveStreamBox">
<h2>节流</h2>
<input type="button" value="搜索" id="searchBtn">
</section>
</section>
</div>
</body>
<script type="text/javascript">
/*将搜索的值存入本地数据库*/
let db = openDatabase("db_search", "1.0", "test prevent shake and save stream", 1024 * 1024 * 2);
let searchBtn = document.getElementById("searchBtn");
let searchBox = document.getElementById("searchBox");
let data = document.getElementById("data");
// 创建数据库
db.transaction(function (transaction) {
transaction.executeSql(" drop table if exists search_history");
transaction.executeSql(" create table search_history ( key varchar(255) unique not null)" ,
[], function () {
console.debug("数据库创建成功");
}, function (transaction, error) {
console.debug("数据库创建失败, 失败原因是: " + error.message);
});
});
window.onload = function () {
// 将搜索的值插入数据库并刷新datalist的值
let saveStreamFlag = true;
searchBtn.addEventListener("click", function () {
if (!saveStreamFlag) {
console.debug("搜索值太频繁");
return false;
}
saveStreamFlag = false;
saveStream();
// 执行 saveStream() 方法的时间不会影响改变 saveStreamFlag标识改变的时间
// 我们可以预估方法执行的时间来改变标识的时间
setTimeout(function () {
saveStreamFlag = true;
}, 1000);
});
// 监听键盘事件模拟搜索场景.
let shakeTimeOut = null;
searchBox.addEventListener("keyup", function () {
let value = searchBox.value.trim();
if (!value) {
return false;
}
// 只要重新输入清空以前的定时任务
if (shakeTimeOut) {
clearTimeout(shakeTimeOut);
}
// 重新定义最新的定时任务
shakeTimeOut = setTimeout(function () {
console.debug(value);
}, 1000);
});
};
// 刷新datalist的值
function flushDataList(transaction) {
transaction.executeSql("select * from search_history", [], function (transaction,resultSet) {
let rows = resultSet.rows;
let options = "";
for (let i = 0; i < rows.length; i++) {
let name = rows[i].key;
console.debug(name);
options += `<option value="${name}">${name}</option>`;
}
data.innerHTML = options;
}, function (transaction, error) {
console.debug("查询历史记录失败, 失败原因为: " + error.message);
});
}
function saveStream() {
const value = searchBox.value.trim();
if (!value) {
console.debug("搜索参数不能为空");
return false;
}
console.debug("参加搜索的值为: " + value);
db.transaction(function (transaction) {
transaction.executeSql("select * from search_history where key = ? ", [new Object(value)], function (transaction, resultSet) {
let rows = resultSet.rows;
if (rows.length <= 0) {
transaction.executeSql("insert into search_history(key) values( ? )", [new Object(value)], function () {
console.debug("插入成功");
}, function (transaction, error) {
console.debug("插入失败, 失败原因: " + error.message);
});
flushDataList(transaction);
}
}, function (transaction, error) {
console.debug("查询历史记录失败, 失败原因为: " + error.message);
});
});
}
</script>
</html>