为什么触发 click 事件侦听器中的 click()不会导致无限循环?

谁能解释一下这段 JavaScript 代码的程序流程:

const $leaveRoom = document.querySelector('#leave-button');
let a = 1;
$leaveRoom.addEventListener('click', () => {
console.log(a);
console.log("check");
a++;
$leaveRoom.click();
console.log(a);
a++;
});
<button id="leave-button">Leave Room</button>

The Output was:
1
check
2
check
3
4

这个问题可能听起来很傻,但我对 JavaScript 还是个新手。我不能理解这段代码的程序流程。我想知道我是如何得到3 & 4的输出。

5132 次浏览

这个问题的关键是在每个 element.click()方法上都存在一个 隐藏的旗帜

每个元素都有一个关联的单击进度标志,该标志最初未设置。

医生: https://html.spec.whatwg.org/multipage/interaction.html#dom-click

一旦这个方法被激活,这个标志就会从 progess Status == unset变成 progess Status == active(伪代码)

(一旦它包含的代码完全执行,它就返回到初始状态)

当此标志处于 active状态时,将忽略对此方法的任何调用。


下面是我的原始文章,展示了‘ console.log ()’的执行顺序

const bt_leaveRoom = document.querySelector('#leave-button')
var counter = 0
var origin  = 'event clic'


bt_leaveRoom.addEventListener('click', () =>
{
let source = origin
console.log(`_first console.log(): counter = ${ ++counter }, origin = ${source}`)


origin = 'call'
bt_leaveRoom.click()
  

console.log(`second console.log(): counter = ${ ++counter }, origin = ${source}`)
})
<button id="leave-button">Leave Room</button>

the Hidden Flag act like in the same way if I have coded this way :
replace this line:
bt_leaveRoom.click()
to:
if (source !== 'call') bt_leaveRoom.click()

But in fact the system use the method hidden flag (named progress flag ?)
which can be (in pseudo code)

if (progress_flag_of_bt_leaveRoom.click() is unset) do { bt_leaveRoom.click() }

为了找到这个问题的答案,我尝试了几种方法。我还没有真正找到一个明确的答案,这里发生了什么,但我相信,我在这里分享的饲料,这个优秀的问题。

我简化了代码,将重点放在事件的递归触发上。

简化代码

const $leaveRoom = document.querySelector('#leave-button');
let a = 1;
$leaveRoom.addEventListener('click', () => {
console.log(a++);
$leaveRoom.click();
});
<button id="leave-button">Leave Room</button>

We can see here that we have two calls for console.log, the first one is for the actual clicking of the button, and the second one is for the call to $leaveRoom.click();. It seems to stop there for some reason.

Using dispatchEvent

const $leaveRoom = document.querySelector('#leave-button');
let a = 1;
$leaveRoom.addEventListener('click', () => {
console.log(a++);
$leaveRoom.dispatchEvent(new Event('click'));
});
<button id="leave-button">Leave Room</button>

Here, the event is triggered multiple times (44 for me), this could be due to how fast your machine is. It seems to stop triggering eventually though, so I'm thinking the same phenomenon is happening here as well.

Using setTimeout

const $leaveRoom = document.querySelector('#leave-button');
let a = 1;
$leaveRoom.addEventListener('click', () => {
console.log(a++);
setTimeout(() => { $leaveRoom.click(); });
});
<button id="leave-button">Leave Room</button>

If you are looking for a way to infinitely trigger the click event, regardless of why the previous methods fail. This does seem to do the trick.

Having said all this, I still have no clue about this hidden force that stops the recursion for the previous methods. Maybe someone could shed some light on this.

const click1 = document.querySelector('#click1')
const click2 = document.querySelector('#click2')
const click3 = document.querySelector('#click3')


click1.addEventListener('click', (event) => {
console.log("click1")
click2.click()
});
click2.addEventListener('click', (event) => {
console.log("click2")
click3.click()
});
click3.addEventListener('click', (event) => {
console.log("click3")
click1.click()
});
<button id="click1">Click 1</button>
<button id="click2">Click 2</button>
<button id="click3">Click 3</button>

事实上,在事件处理中似乎存在循环破坏逻辑。处理程序将愉快地连接用户输入事件,直到最初的交互分发重复事件。那它就不会再派遣任何人了。