单击只运行一次的EventListener



我还在学习JavaScript的基础知识,目前正在学习eventListeners。我试图做一个按钮,点击改变整个身体的背景颜色一些随机生成的rgb代码,每100毫秒,并再次点击它,背景颜色变回白色,停止颜色变化。

我用setTimeout创建了一个循环。当单击按钮时,生成随机rgb值并应用于正文背景色。我使用了一个布尔标志,当再次单击按钮时,它被赋予假值,它在检查if条件时停止循环。我所面临的问题是,事件监听器不工作超过一次点击。

代码如下:

const button = document.querySelector('#btn');
var flag = true;
button.addEventListener('click', function() {
loop();
})
function makeRGB() {
const r = Math.floor(Math.random() * 255);
const g = Math.floor(Math.random() * 255);
const b = Math.floor(Math.random() * 255);
const colorID = `rgb(${r},${g},${b})`;
document.body.style.backgroundColor = colorID;
}
function loop() {
if (!flag) {
return;
}
makeRGB();
setTimeout(loop, 100);
button.onclick = function stop() {
flag = false;
document.body.style.backgroundColor = 'white';
}
}
h1 {
text-align: center;
}
button {
margin: auto;
display: block;
}
<h1 id="heading">Welcome!</h1>
<button id="btn">Change Color! </button>

我使用内置的setInterval函数,而不是创建我们自己的临时间隔。setIntervalsetTimeout都返回一个数字,你可以传递给clearIntervalclearTimeout来停止异步代码的执行。

const button = document.querySelector('#btn');
let changeColorInterval;
button.addEventListener('click', function() {
// if we currently have an interval running, e.g. != undefined
if (changeColorInterval) {
// remove the interval, e.g. stop the execution of makeRGB
clearInterval(changeColorInterval);
// set the variable back to undefined, otherwise next time
// you click the button this branch of the if statement
// will be executed, even though the interval is not
// actually running anymore.
changeColorInterval = undefined;
// restore the background to white like you wanted.
document.body.style.backgroundColor = "white";

// If we don't have an interval, create one
} else changeColorInterval = setInterval(makeRGB, 100);
})
function makeRGB() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
const colorID = `rgb(${r},${g},${b})`;
document.body.style.backgroundColor = colorID;
}
h1 {
text-align: center;
}
button {
margin: auto;
display: block;
}
<h1 id="heading">Welcome!</h1>
<button id="btn">Change Color! </button>

为什么它似乎只被调用一次

if (!flag) {返回;}

每次按下按钮时都会调用事件侦听器本身。你可以看到,如果你把一个console.log("click")放在你的点击回调中,就在loop()之前。问题是,你从来没有分配你的变量回true,所以它总是存在的函数。

不要在事件监听器中添加事件监听器…除非你真的想

按钮。Onclick = function stop() {Flag = false;document.body.style.backgroundColor = '白色';}

你分配了一个"老派风格"事件监听器中的事件监听器,这似乎不是个好主意。参见:addEventListener vs onclick

为什么不使用var

您很可能不想使用var。您很可能永远不想使用var。只使用let来声明要修改的变量,使用const来声明应该是常量的变量。如果你真的关心为什么谷歌"javascript var vs let vs construct"之类的东西。但是如果你刚刚开始学习javascript,最好在你理解它之前避免使用var

颜色生成错误

你的颜色生成只是有点错误。如mozilla

所述

Math.random()函数返回一个0到小于1的浮点伪随机数(包括0,但不包括1)

所以0 <= Math.random() < 1。你永远不会得到1,因为上界是独占的.

假设你得到9.99999999...,然后乘以255。你永远不会得到255,而是比它更小的东西。然后踩油门。所以你得到的最大值是254。为了解决这个问题,我建议乘以256

为额外的帮助写注释

如果你需要任何额外的帮助来理解代码,请在这个答案的评论中回复我:)

  1. 让你的按钮处理程序返回一个闭包来维持你的标志状态。这样你就不需要任何全局变量了。

  2. 使用setTimeout,并且只在满足条件时调用它。这样你就不需要clear任何东西了。

  3. makeRGB返回一个值,而不是直接设置元素的颜色。

const button = document.querySelector('#btn');
// When you call `handler` it returns a new function 
// that is called when the button is clicked
button.addEventListener('click', handler(), false);
function makeRGB() {
const r = Math.floor(Math.random() * 255);
const g = Math.floor(Math.random() * 255);
const b = Math.floor(Math.random() * 255);
return `rgb(${r},${g},${b})`;
}
// Initially set `flag` to false
function handler(flag = false) {
// Cache the document body element
const body = document.body;
// Return the function that serves as
// the click listener
return function() {
// Reset the flag when the button is clicked
flag = !flag
// Start the loop
function loop() {
// If `flag` is true set the new colour
// and call `loop` again
if (flag) {
body.style.backgroundColor = makeRGB();
setTimeout(loop, 100);
// Otherwise set the background to white
} else {
body.style.backgroundColor = 'white';
}
}
loop();
}
}
h1 { text-align: center; }
button { margin: auto; display: block; }
<h1 id="heading">Welcome!</h1>
<button id="btn">Change Color! </button>

使用setInterval代替setTimeout以便不断改变背景颜色,直到再次点击按钮

const button = document.querySelector('#btn');
var flag = false;
var startChange;
button.addEventListener('click', function() {
flag = !flag;
loop();
})
function makeRGB() {
const r = Math.floor(Math.random() * 255);
const g = Math.floor(Math.random() * 255);
const b = Math.floor(Math.random() * 255);
const colorID = `rgb(${r},${g},${b})`;
document.body.style.backgroundColor = colorID;
}
function loop() {
if (!flag) {
clearInterval(startChange);
document.body.style.backgroundColor = 'white';
return;
}
startChange = setInterval(makeRGB, 100);
}
h1 {
text-align: center;
}
button {
margin: auto;
display: block;
}
<h1 id="heading">Welcome!</h1>
<button id="btn">Change Color! </button>

这是您的代码的重新格式化。也许可以再清理一下,但应该按你的意愿去做。这里我们不覆盖间隔,而是删除它。

const randomColor = () => Array(3).fill(0).map(() => Math.floor(Math.random() * 256)).join();
let loop;
document.querySelector("#btn").addEventListener("click", (e) => {
if(loop){
clearInterval(loop);
loop = undefined;
document.body.style.backgroundColor = "white";
} else {
loop = setInterval(() => {
document.body.style.backgroundColor = `rgb(${randomColor()})`;
}, 100);
}
})

最新更新