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

Why doesn't triggering click() inside a click event listener cause an infinite loop?

谁能解释一下这个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 的。

The key to this question is the presence of a hidden Flag on each element.click() method.

Each element has an associated click in progress flag, which is initially unset.

文档:https://html.spec.whatwg.org/multipage/interaction.html#dom-click

只要这个方法被激活,这个flag就会从progess Status == unset变成progess Status == active (伪代码)

(然后它 returns 到它包含的代码完全执行后的初始状态)

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


这是我原来的 post 来显示 `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>

如果我这样编码,Hidden Flag 的作用与此相同:
替换此行:
bt_leaveRoom.click()
至:
if (source !== 'call') bt_leaveRoom.click()

但实际上系统使用隐藏标志的方法(名为progress flag?)
可以是(伪代码)

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>

我们可以在这里看到我们有两个调用 console.log,第一个是实际点击按钮,第二个是对 $leaveRoom.click(); 的调用。它似乎因某种原因停在那里。

使用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>

这里,事件被触发了多次(我是 44 次),这可能是因为你的机器速度太快了。它似乎最终停止触发,所以我认为同样的现象也发生在这里。

使用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>

如果您正在寻找一种无限触发点击事件的方法,而不管之前的方法为何失败。这似乎确实可以解决问题。

说了这么多,我仍然不知道这种阻止以前方法递归的隐藏力量。也许有人可以阐明这一点。

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>

实际上事件处理中似乎存在循环中断逻辑。处理程序将愉快地菊花链用户输入事件,直到初始交互分派重复事件。然后就不再派发了。