了解 JavaScript 中禁用按钮上的排队点击事件

问题描述 投票:0回答:1

我对以下代码片段的行为感到困惑。我有一个按钮,在单击后运行长时间计算时会被禁用。

        function delay() {
            console.log('start');
            for (let x = 0; x < 4000000000; x++) {
                Math.sqrt(20);
            }
            console.log('done');
        }

        function click() {
            button.disabled = true;
            setTimeout(() => {
                delay();
                button.disabled = false;
            }, 0);
        }
        const button = document.getElementById('button');
        button.addEventListener('click', click);

按钮成功禁用并且运行了很长的计算,但是发生了一些奇怪的事情:如果我在按钮处于禁用状态时多次单击按钮,它会将单个单击事件排队并在完成后再次运行 click() 函数当前计算。

我的理解是禁用的元素不应触发或排队任何事件。谁能解释一下这种行为吗?

我已经阅读了 JavaScript 的微任务和宏任务以及 UI 如何在它们之间更新。基于此,我预计按钮在禁用时对点击完全没有响应。

但是,我观察到,尽管按钮被禁用,但如果我在禁用按钮时多次单击它,单个单击事件仍然会排队。

javascript html dom dom-events web-frontend
1个回答
0
投票

它与浏览器如何处理快速事件序列和元素的禁用状态有关。

当您快速单击该按钮时,即使该按钮被禁用,由于浏览器快速连续处理事件的方式,浏览器仍可能会注册单击事件。如果按钮被禁用然后在短时间内重新启用(就像您的代码一样),则尤其如此。

以下是所发生事件的详细说明:

您单击按钮。 调用点击函数。 该按钮立即被禁用。 setTimeout 函数安排延迟函数在当前调用堆栈被清除后运行。

当按钮被禁用时,您再次单击它。由于事件发生的速度很快,浏览器可能仍会注册此单击事件。 延迟功能运行,完成后按钮重新启用。 即使在按钮禁用时注册了排队的单击事件,也会处理该事件。 为了避免这种行为,您可以使用标志来检查计算是否已经在运行并防止再次调用该函数:

    let isRunning = false;

function delay() {
    console.log('start');
    for (let x = 0; x < 4000000000; x++) {
        Math.sqrt(20);
    }
    console.log('done');
}

function click() {
    if (isRunning) return;

    isRunning = true;
    button.disabled = true;
    setTimeout(() => {
        delay();
        button.disabled = false;
        isRunning = false;
    }, 0);
}

const button = document.getElementById('button');
button.addEventListener('click', click);

通过此修改,即使浏览器在按钮禁用时注册了单击事件,单击函数也会立即返回而不执行任何操作,因为 isRunning 标志设置为 true。

© www.soinside.com 2019 - 2024. All rights reserved.