文章目录
区别
1.同代码对比
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
//变量初始化
var num1 = 0;
var num2 = 0;
var clock1;
var clock2;
//定义启动器
function work(ele) {
if (ele.id === 'box1') {
ele.style.width = num1 + 'px';
if (ele.getAttribute('data-toggle') === 'off') { //相等判断则为真,严格相等则为假,除非加双引号
num1 += 1;
if (num1 === 200) {
ele.setAttribute('data-toggle', 'on');
}
} else if (ele.getAttribute('data-toggle') == 'on') {
num1 -= 1;
if (num1 === 0) {
ele.setAttribute('data-toggle', 'off');
}
}
} else {
ele.style.width = num2 + 'px';
if (ele.getAttribute('data-toggle') == 'off') { //相等判断则为真,严格相等则为假,除非加双引号
num2 += 1;
if (num2 === 200) {
ele.setAttribute('data-toggle', 'on');
}
} else if (ele.getAttribute('data-toggle') == 'on') {
num2 -= 1;
if (num2 === 0) {
ele.setAttribute('data-toggle', 'off');
}
}
}
}
//定义暂停器
function rest() {
if (this.id === 'stop1') {
clearTimeout(clock1);
} else {
clearInterval(clock2);
}
}
window.onload = function() {
//第一个盒子
var box1 = document.getElementById('box1');
document.getElementById('start1').onclick = function() {
clock1 = setTimeout(function() {
work(box1);
}, 10);
}
document.getElementById('stop1').onclick = rest;
//第二个盒子
var box2 = document.getElementById('box2');
document.getElementById('start2').onclick = function() {
clock2 = setInterval(function() {
work(box2);
}, 10);
}
document.getElementById('stop2').onclick = rest;
}
</script>
</head>
<body>
<h3>setTimeout() 与 setInterval() 的区别</h3>
<input type="button" id="start1" value="setTimeout" />
<input type="button" id="stop1" value="clearTimeout" />
<div id="box1" style="width: 0;height: 50px;background: red" data-toggle="off"></div>
<input type="button" id="start2" value="setInterval" />
<input type="button" id="stop2" value="clearInterval" />
<div id="box2" style="width: 0;height: 50px;background: green" data-toggle="off"></div>
</body>
</html>
-
小结:
- 1.相同条件下,setTimeout() 只执行一次,setInterval() 则循环执行;
-
2.setTimeout() 延迟执行一次:
setTimeout(fn, 1000); //延迟1秒,执行一次fn();
-
3.setInterval() 隔段时间循环执行;
setInterval(fn, 1000); //每隔1秒,循环执行fn()
2.setTimeout() 模拟 setInterval() 【缺陷】(已修复:2020/09/07,知识点:防抖)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
//变量初始化
var num1 = 0;
var num2 = 0;
var clock1;
var clock2;
//定义启动器
function work(ele) {
if (ele.id === 'box1') {
ele.style.width = num1 + 'px';
if (ele.getAttribute('data-toggle') === 'off') { //相等判断则为真,严格相等则为假,除非加双引号
num1 += 1;
if (num1 === 200) {
ele.setAttribute('data-toggle', 'on');
}
} else if (ele.getAttribute('data-toggle') == 'on') {
num1 -= 1;
if (num1 === 0) {
ele.setAttribute('data-toggle', 'off');
}
}
clock1 = setTimeout(function() {
work(ele);
}, 10);
} else {
ele.style.width = num2 + 'px';
if (ele.getAttribute('data-toggle') == 'off') { //相等判断则为真,严格相等则为假,除非加双引号
num2 += 1;
if (num2 === 200) {
ele.setAttribute('data-toggle', 'on');
}
} else if (ele.getAttribute('data-toggle') == 'on') {
num2 -= 1;
if (num2 === 0) {
ele.setAttribute('data-toggle', 'off');
}
}
}
}
//定义暂停器
function rest() {
if (this.id === 'stop1') {
clearTimeout(clock1);
} else {
clearInterval(clock2);
}
}
window.onload = function() {
//第一个盒子
var box1 = document.getElementById('box1');
document.getElementById('start1').onclick = function() {
// 可查阅关键字【防抖】的文章
// clearTimeout(clock1); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 经过提醒,可以在此处清除计时器,来阻止叠加执行
work(box1);
};
document.getElementById('stop1').onclick = rest;
//第二个盒子
var box2 = document.getElementById('box2');
document.getElementById('start2').onclick = function() {
// 可查阅关键字【防抖】的文章
// clearInterval(clock2); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 经过提醒,可以在此处清除计时器,来阻止叠加执行
clock2 = setInterval(function() {
work(box2);
}, 10);
}
document.getElementById('stop2').onclick = rest;
}
</script>
</head>
<body>
<h3>setTimeout() 与 setInterval() 的区别</h3>
<input type="button" id="start1" value="setTimeout" />
<input type="button" id="stop1" value="clearTimeout" />
<div id="box1" style="width: 0;height: 50px;background: red" data-toggle="off"></div>
<input type="button" id="start2" value="setInterval" />
<input type="button" id="stop2" value="clearInterval" />
<div id="box2" style="width: 0;height: 50px;background: green" data-toggle="off"></div>
</body>
</html>
注意:反复点击按钮,计时器会被叠加执行,使得数值抽搐跳动(下面有介绍如何解决)
只有 setTimeout() 会被 clearTimeout() 或 clearInterval() 依次停止,而 setInterval() 则无法被停止。
3.setTimeout() + for
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
//定义宽递增动画
function addWidth(ele) {
for (var i = parseInt(ele.style.width); i <= 200; i++) {
(function(i) {
setTimeout(function() {
ele.style.width = i + 'px';
// console.log(i);
if (i === 200) {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
}
}, i*10);
})(i);
}
}
//定义宽递减动画
function redWidth(ele) {
var j = 0;
for (var i = parseInt(ele.style.width); i >= 0; i--) {
(function(i) {
setTimeout(function() {
ele.style.width = i + 'px';
// console.log(i);
if (i === 0) {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
}
}, j*10);
})(i);
j++;
}
}
// 创建启动器
function work(ele) {
// 当宽为0时, 递增开关值为'up'
if (parseInt(ele.style.width) === 0) {
ele.setAttribute('data-toggle', 'up');
// 当宽为200时, 递减开关值为'down'
} else if (parseInt(ele.style.width) === 200) {
ele.setAttribute('data-toggle', 'down');
}
//未被禁止时执行
if (ele.getAttribute('data-disabled') === null) {
// 开关为'up'时
if (ele.getAttribute('data-toggle') === 'up') {
addWidth(ele);
// 开关为'down'时
} else {
redWidth(ele);
}
}
// 点击启动按钮后,设置'data-disabled', 避免重复点击导致计时器叠加执行
ele.setAttribute('data-disabled', true);
}
window.onload = function() {
var box = document.getElementById('box');
document.getElementById('start').onclick = function() {
work(box);
}
}
</script>
</head>
<body>
<input type="button" id="start" value="work" />
<div id="box" style="width: 0;height: 50px;background: pink"></div>
</body>
</html>
又或者
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
// 定义宽递增动画
function addWidth(i,ele) {
setTimeout(function() {
ele.style.width = i + 'px';
// console.log('up: ' + i);
}, i*10);
}
// 定义宽递减动画
function redWidth(i,j,ele) {
setTimeout(function() {
ele.style.width = i + 'px';
// console.log('down: ' + i);
}, j*10);
}
// 定义启动器
function work(ele) {
// i: 当前宽值
var i = parseInt(ele.style.width);
// j: 递减时控制时间, 因为数值递减,执行时间递增
var j = 0;
if (parseInt(ele.style.width) === 0) {
for (i; i <= 200; i++) {
addWidth(i,ele);
}
} else if (parseInt(ele.style.width) === 200) {
for (i; i >= 0; i--) {
redWidth(i,j,ele);
j += 1;
}
}
}
window.onload = function() {
var box = document.getElementById('box');
document.getElementById('start').onclick = function() {
work(box)
}
}
</script>
</head>
<body>
<h3>通过for循环控制setTimeout()</h3>
<input type="button" id="start" value="start" />
<div id="box" style="width: 0;height: 50px;background: pink"></div>
</body>
</html>
解决与演示
通过改良上例代码,实现以下功能:
- 通过点击按钮控制宽值递增或递减;
- 当宽值达到最小值 0px 或最大值 200px 时,函数自动结束(含停止递增或递减);
- 按钮控制,宽值递增达到 200px 时,切换为递减;宽值递减达到 0px 时,切换递增;可周而复始执行;
- 反复执行按钮不会造成函数重复执行,导致宽值来回抽搐跳动;
- 可通过暂停按钮来中断宽值递增或递减;
setInterval() 写法
code(因为最先写的,所以冗余过多,还未压缩删减)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
/*
Tips:
1. addWidth() 表示宽值递增时执行的函数;
addWidth() 表示宽值递增时执行的函数;
2. 'ele' 表示被传入的DOM节点, 而所有函数的 'ele' 均继承 'work()' 如:work(box);
3. 'timer1' 是 addWidth() 使用的计时器;
'timer2' 是 redWidth() 使用的计时器;
4. data-disabled="true" 避免重复点击导致计时器叠加执行, 点击启动时设置,点击暂停时移除;
5. data-toggle="up" / data-toggle="down" 分别表示未被终止过的递增和递减开关;
data-toggle="add" / data-toggle="red" 分别表示被终止过的递增和递减开关;
6. setInterval(fn, 10), 每隔10毫秒(0.01)秒执行;
7. var i; 表示从 0 或 200 开始计数;
var j; 表示从 当前宽+1 或 当前宽-1 开始计数;
8. 'j = parseInt(ele.style.width) + 1' 表示被终止过的递增, 获取当前宽值, 并从下一个数开始执行;
'j = parseInt(ele.style.width) - 1' 表示被终止过的递减, 获取当前宽值, 并从下一个数开始执行;
+1 / -1, 避免重复执行当前值;
9. 结束 setInterval / setTimeout , 要用 clearTimeout / clearInterval, return 无效;
10. 如有错误, 欢迎留言指正;
测试序号 A1/B1: 用于打印数值, 检测 i 和 j 值是否连续;
测试序号 A2/B2: 用于测试反复点击按钮, 是否出现重值现象, 是否出现跳值现象;
测试序号 A3/B3: 事实证明, return 对于计数器无效, 只能用 clearInterval 结束, 这里把我坑惨了;
*/
// 定义计时器名
var timer1;
var timer2;
// 宽递增
function addWidth(ele) {
// i 值:data-toggle="up" 时使用, 从 0 开始计数
var i = 0;
// j 值:data-toggle="add" 时使用, 从 (当前宽+1) 开始计数
var j = parseInt(ele.style.width) + 1;
// 当宽为0时执行
if (parseInt(ele.style.width) === 0) {
// 执行 timer1 计时器
timer1 = setInterval(function() {
// i 小于200时, 递增
if (i < 200) {
i += 1;
ele.style.width = i + 'px';
// console.log('up_i: ' + i); // 打印 i 值 >>>>>> 测试序号 A1
// i 值等于200时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
// console.log('end_i: ' + i); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 A3
// return; // 这是错误的 >>>>>> 测试序号 A3
clearInterval(timer1); // 注释该条再测试 >>>>>> 测试序号 A3
}
// console.log('请反复点击按钮(up):' + i); // 检测是否叠加执行 >>>>>> 测试序号 A2
}, 15);
// 当被终止过递增, 且无'data-disabled'时执行
} else if (ele.getAttribute('data-toggle') === 'add' && ele.getAttribute('data-disabled') === null) {
// 执行 timer1 计时器
timer1 = setInterval(function() {
// j 小于200时, 递增
if (j < 200) {
j += 1;
ele.style.width = j + 'px';
// console.log('add_j: ' + j); // 打印 j 值 >>>>>> 测试序号 A1
// j 值等于200时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
// console.log('end_j: ' + j); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 A3
// return; // 这是错误的 >>>>>> 测试序号 A3
clearInterval(timer1); // 注释该条再测试 >>>>>> 测试序号 A3
}
// console.log('请反复点击按钮(add):' + j); // 检测是否叠加执行 >>>>>> 测试序号 A2
}, 15);
}
}
// 宽递减
function redWidth(ele) {
// i值:data-toggle="down" 时使用, 从 0 开始计数
var i = 200;
// j值:data-toggle="red" 时使用, 从 (当前宽-1) 开始计数
var j = parseInt(ele.style.width) - 1;
// 当宽为200时执行
if (parseInt(ele.style.width) === 200) {
// 执行 timer2 计时器
timer2 = setInterval(function() {
// i 大于0时, 递减
if (i > 0) {
i -= 1;
ele.style.width = i + 'px';
// console.log('down_i: ' + i); // 打印 i 值 >>>>>> 测试序号 B1
// i 值等于0时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
// console.log('end_i: ' + i); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 B3
// return; // 这是错误的 >>>>>> 测试序号 B3
clearInterval(timer2); // 注释该条再测试 >>>>>> 测试序号 B3
}
// console.log('请反复点击按钮(down):' + i); // 检测是否叠加执行 >>>>>> 测试序号 B2
}, 15);
// 当被终止过递减, 且无'data-disabled'时执行
} else if (ele.getAttribute('data-toggle') == 'red' && ele.getAttribute('data-disabled') === null) {
// 执行 timer2 计时器
timer2 = setInterval(function() {
// j 大于0时, 递减
if (j > 0) {
j -= 1;
ele.style.width = j + 'px';
// console.log('red_j: ' + j); // 打印 j 值 >>>>>> 测试序号 B1
// j 值等于0时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
// console.log('end_j: ' + j); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 B3
// return; // 这是错误的 >>>>>> 测试序号 B3
clearInterval(timer2); // 注释该条再测试 >>>>>> 测试序号 B3
}
// console.log('请反复点击按钮(red):' + j); // 检测是否叠加执行 >>>>>> 测试序号 B2
}, 15);
}
}
// 创建启动器
function work(ele) {
// 当宽为0时, 默认递增开关值为'up'
if (parseInt(ele.style.width) === 0) {
ele.setAttribute('data-toggle', 'up');
// 当宽为200时, 默认递减开关值为'down'
} else if (parseInt(ele.style.width) === 200) {
ele.setAttribute('data-toggle', 'down');
}
// 当开关为up/add时执行, up表示默认, add表示被终止过
if (ele.getAttribute('data-toggle') === 'up' || ele.getAttribute('data-toggle') === 'add') {
addWidth(ele);
// 当开关为down/red时执行, down表示默认, red表示被终止过
} else if (ele.getAttribute('data-toggle') === 'down' || ele.getAttribute('data-toggle') === 'red') {
redWidth(ele);
}
// 点击启动按钮后,设置'data-disabled', 避免重复点击导致计时器叠加执行
ele.setAttribute('data-disabled', true);
}
// 创建暂停器
function rest(ele) {
// 当递增时执行
if (ele.getAttribute('data-toggle') == 'up' || ele.getAttribute('data-toggle') == 'add'){
// 停止计时
clearInterval(timer1);
// 切换递增名, add表示被终止过
ele.setAttribute('data-toggle', 'add');
// 当递减时执行
} else if (ele.getAttribute('data-toggle') == 'down' || ele.getAttribute('data-toggle') == 'red') {
// 停止计时
clearInterval(timer2);
// 切换递增名, red表示被终止过
ele.setAttribute('data-toggle', 'red');
}
// 点击暂停按钮后,移除'data-disabled', 使启动按钮可点击并执行
ele.removeAttribute('data-disabled');
}
window.onload = function() {
var box = document.getElementById('box');
document.getElementById('start').onclick = function() {
work(box);
}
document.getElementById('stop').onclick = function() {
rest(box);
};
}
</script>
</head>
<body>
<input type="button" id="start" value="work" />
<input type="button" id="stop" value="rest" />
<div id="box" style="width: 0;height: 50px;background: green"></div>
</body>
</html>
demo(未改良)
code(已改良)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
/*
Tips:
1. addWidth() 表示宽值递增时执行的函数;
addWidth() 表示宽值递增时执行的函数;
2. 'ele' 表示被传入的DOM节点, 而所有函数的 'ele' 均继承 'work()' 如:work(box);
3. 'timer1' 是 addWidth() 使用的计时器;
'timer2' 是 redWidth() 使用的计时器;
4. data-disabled="true" 避免重复点击导致计时器叠加执行, 点击启动时设置,点击暂停时移除;
5. data-toggle="up" / data-toggle="down" 分别递增和递减开关;
6. setInterval(fn, 15), 每隔15毫秒(0.015)秒执行;
7. i值: 当前宽值;
8. 结束 setInterval / setTimeout , 要用 clearTimeout / clearInterval, return 无效;
9. 如有错误, 欢迎留言指正;
测试序号 A1/B1: 用于打印数值, 检测 i 是否连续;
测试序号 A2/B2: 用于测试反复点击按钮, 是否出现重值现象, 是否出现跳值现象;
测试序号 A3/B3: 事实证明, return 对于计数器无效, 只能用 clearInterval 结束, 这里把我坑惨了;
*/
// 定义计时器名
var timer1;
var timer2;
// 宽递增
function addWidth(ele) {
// i值: 当前宽值
var i = parseInt(ele.style.width);
// 执行 timer1 计时器
timer1 = setInterval(function() {
// i 小于等于200时, 递增
if (i < 200) {
i += 1;
ele.style.width = i + 'px';
console.log('up_i: ' + i); // 打印 i 值 >>>>>> 测试序号 A1
// i 值等于200时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
// console.log('end_i: ' + i); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 A3
// return; // 这是错误的 >>>>>> 测试序号 A3
clearInterval(timer1); // 注释该条再测试 >>>>>> 测试序号 A3
}
// console.log('请反复点击按钮(up):' + i); // 检测是否叠加执行 >>>>>> 测试序号 A2
}, 15);
}
// 宽递减
function redWidth(ele) {
// i值: 当前宽值
var i = parseInt(ele.style.width);
// 执行 timer2 计时器
timer2 = setInterval(function() {
// i 大于等于0时, 递减
if (i > 0) {
i -= 1;
ele.style.width = i + 'px';
console.log('down_i: ' + i); // 打印 i 值 >>>>>> 测试序号 B1
// i 值等于0时, 解除禁止并结束当前计时
} else {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
// console.log('end_i: ' + i); // 检查结束后, 该值是否继续打印 >>>>>> 测试序号 B3
// return; // 这是错误的 >>>>>> 测试序号 B3
clearInterval(timer2); // 注释该条再测试 >>>>>> 测试序号 B3
}
// console.log('请反复点击按钮(down):' + i); // 检测是否叠加执行 >>>>>> 测试序号 B2
}, 15);
}
// 创建启动器
function work(ele) {
// 当宽为0时, 递增开关值为'up'
if (parseInt(ele.style.width) === 0) {
ele.setAttribute('data-toggle', 'up');
// 当宽为200时, 递减开关值为'down'
} else if (parseInt(ele.style.width) === 200) {
ele.setAttribute('data-toggle', 'down');
}
//未被禁止时执行
if (ele.getAttribute('data-disabled') === null) {
// 开关为'up'时
if (ele.getAttribute('data-toggle') === 'up') {
addWidth(ele);
// 开关为'down'时
} else {
redWidth(ele);
}
}
// 点击启动按钮后,设置'data-disabled', 避免重复点击导致计时器叠加执行
ele.setAttribute('data-disabled', true);
}
// 创建暂停器
function rest(ele) {
// 当递增时执行
if (ele.getAttribute('data-toggle') == 'up'){
// 停止计时
clearInterval(timer1);
// 当递减时执行
} else if (ele.getAttribute('data-toggle') == 'down') {
// 停止计时
clearInterval(timer2);
}
// 点击暂停按钮后,移除'data-disabled', 使启动按钮可点击并执行
ele.removeAttribute('data-disabled');
}
window.onload = function() {
var box = document.getElementById('box');
document.getElementById('start').onclick = function() {
work(box);
}
document.getElementById('stop').onclick = function() {
rest(box);
};
}
</script>
</head>
<body>
<input type="button" id="start" value="work" />
<input type="button" id="stop" value="rest" />
<div id="box" style="width: 0;height: 50px;background: green"></div>
</body>
</html>
demo(已改良)
setTimeout() 写法(更简单)
code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<script type="text/javascript">
var timer1;
var timer2;
//定义宽递增动画
function addWidth(ele) {
var i = parseInt(ele.style.width);
if (i < 200) {
i += 1;
ele.style.width = i + 'px';
// console.log(i);
timer1 = setTimeout(function() {
addWidth(ele);
}, 15);
} else {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
}
}
//定义宽递减动画
function redWidth(ele) {
var i = parseInt(ele.style.width);
if (i > 0) {
i -= 1;
ele.style.width = i + 'px';
// console.log(i);
timer2 = setTimeout(function() {
redWidth(ele);
}, 15);
} else {
ele.removeAttribute('data-disabled');
ele.removeAttribute('data-toggle');
}
}
// 创建启动器
function work(ele) {
// 当宽为0时, 递增开关值为'up'
if (parseInt(ele.style.width) === 0) {
ele.setAttribute('data-toggle', 'up');
// 当宽为200时, 递减开关值为'down'
} else if (parseInt(ele.style.width) === 200) {
ele.setAttribute('data-toggle', 'down');
}
//未被禁止时执行
if (ele.getAttribute('data-disabled') === null) {
// 开关为'up'时
if (ele.getAttribute('data-toggle') === 'up') {
addWidth(ele);
// 开关为'down'时
} else {
redWidth(ele);
}
}
// 点击启动按钮后,设置'data-disabled', 避免重复点击导致计时器叠加执行
ele.setAttribute('data-disabled', true);
}
// 创建暂停器
function rest(ele) {
// 当递增时执行
if (ele.getAttribute('data-toggle') == 'up'){
// 停止计时
clearInterval(timer1);
// 当递减时执行
} else if (ele.getAttribute('data-toggle') == 'down') {
// 停止计时
clearInterval(timer2);
}
// 点击暂停按钮后,移除'data-disabled', 使启动按钮可点击并执行
ele.removeAttribute('data-disabled');
}
window.onload = function() {
var box = document.getElementById('box');
document.getElementById('start').onclick = function() {
work(box);
}
document.getElementById('stop').onclick = function() {
rest(box);
};
}
</script>
</head>
<body>
<input type="button" id="start" value="work" />
<input type="button" id="stop" value="rest" />
<div id="box" style="width: 0;height: 50px;background: red"></div>
</body>
</html>
demo
setTimeout() + for
关于此方法,尝试多次后发现,暂停过再重新执行,数值不连续,来回跳动,计时器叠加执行,还未能找到解决方法。真是百思不得其解。
因为,for遍历的值是同一时间打印出来的,而无法依次被 setTimeout 执行;还有循环过后的变量会影响第二次点击的值,所以造成数值不连续执行。