You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JavaScript计时器在keydown事件触发时停止?求技术解析

问题解析:为何keydown事件会触发计时器停止?

我通过keyup事件启动计时器,但无法理解为何keydown事件触发时计时器会停止。该功能虽符合预期,但我并未在keydown事件监听器中调用stopWatch函数,排查所有可能场景后仍不明就里,恳请解析原因。

相关代码

let cubeTimer = document.getElementById("cubeTimer");
let appendMinutes = document.getElementById("minutes");
let appendSeconds = document.getElementById("seconds");
let appendMilliseconds = document.getElementById("milliseconds");
let scramble = document.getElementById("scramble");

let minutes = 0;
let seconds = 0;
let milliseconds = 0.00;
let start = true;
let stop = false;
let arr = [];
let interval;
let scrambleArray = ["R", "L", "R'", "L'", "U", "D", "U'", "D'", "F", "B", "F'", "B'", "R2", "L2", "U2", "B2", "D2", "F2"];

function scrambleCube () {
    let newArr = []
    for (let i = 0; i < 21; i++) {
        if (newArr.length >= 1) {
            newArr.push(scrambleArray[Math.floor(Math.random() * 18)]);
            do {
                newArr[i] = scrambleArray[Math.floor(Math.random() * 18)]
            } while (newArr[i][0] === newArr[i - 1][0]);
        }; 
        if (newArr.length < 1) {
            newArr.push(scrambleArray[Math.floor(Math.random() * 18)]);
        };
        
    };

    for (let i = 0; i < newArr.length; i++) {
        newArr[i] = " " + newArr[i]
    };

    scramble.innerHTML = newArr;
}
scrambleCube()

document.addEventListener("keydown", () => {
    
    arr.push("a");
    if (arr.length > 10) {
        cubeTimer.style.color = "green";
        cubeTimer.style.fontSize = "40px";
        start = true;
        stop = false;
        minutes = 0;
        seconds = 0;
        milliseconds = 0.00;
        appendMinutes.innerHTML = "00";
        appendSeconds.innerHTML = "00";
        appendMilliseconds.innerHTML = "00";
    } else if (arr.length < 10) {
        cubeTimer.style.color = "red";
    }

    if (start === false) { stop = true };
    
});

document.addEventListener("keyup", () => {
    start = false;
    cubeTimer.style.color = "green";
    if (arr.length > 10) {
        interval = setInterval(stopWatch, 10);    
    }
    arr = [];
});

function stopWatch () {
    if (stop === true) {  
        clearInterval(interval);
        scrambleCube()
    };

    milliseconds++;

    if (milliseconds == 100) {
        seconds++;
        milliseconds = 0;
    };
    
    if  (seconds == 60) {
        minutes++;
        seconds = 0;
    };

    let milliString = milliseconds;
    let secondString = seconds;
    let minuteString  = minutes;
    
    if (minutes < 10) {
        minuteString = "0" + minuteString;
    };

    if (seconds < 10) {
        secondString = "0" + secondString;
    };

    if (milliseconds < 10) {
        milliString = "0" + milliString;
    };

    appendMinutes.innerHTML = minuteString;
    appendSeconds.innerHTML = secondString;
    appendMilliseconds.innerHTML = milliString;
};

核心原因

你确实没直接调用stopWatch,但keydown事件里修改了stop变量的状态,而stopWatch函数会每隔10毫秒自动检查stop变量的值,一旦发现stop为true,就执行clearInterval(interval)停止计时器。

具体流程拆解

  1. 启动计时器:松开按键触发keyup时,start被设为false,同时启动setInterval(stopWatch, 10),此时stop是false,计时器正常运行。
  2. 触发keydown:再次按下按键时,keydown事件监听器里执行if (start === false) { stop = true };——此时start在之前的keyup中已经被设为false,所以stop被改为true。
  3. 计时器自动检测停止:正在运行的stopWatch函数每10毫秒执行一次,它的第一行就是检查stop === true,一旦满足条件就立刻清除计时器,所以看起来像是keydown直接停止了计时器,实际上是通过变量状态的间接触发。

补充说明

这种设计符合魔方计时器的常见逻辑(按住按键准备,松开开始计时,再次按下停止计时),但start/stop的变量命名逻辑有点绕,建议可以优化变量名让逻辑更清晰,比如用isRunning表示计时器是否在运行状态。

内容的提问来源于stack exchange,提问作者Mritz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 19:16:20