使用合成的“Esc”键关闭模态<dialog>元素

3
我正在尝试通过模拟按下“escape”键来关闭一个模态对话框<dialog>元素。我将在下面解释这个例子:

document.addEventListener("keydown", (e) => {
  console.log(e)
});
document.addEventListener("keyup", (e) => {
  console.log(e)
});
document.addEventListener("keypress", (e) => {
  console.log(e)
});

const dialog = document.querySelector(`dialog`);

const openDialogButton = document.querySelector(`#open-dialog-button`);
openDialogButton.addEventListener("click", () => {
  dialog.showModal();
});

const syntheticEscapeButton = document.querySelector(`#synthetic-escape-button`);
syntheticEscapeButton.addEventListener("click", () => {
  document.dispatchEvent(
    new KeyboardEvent("keydown", {
      altKey: false,
      code: "Escape",
      ctrlKey: false,
      isComposing: false,
      key: "Escape",
      location: 0,
      metaKey: false,
      repeat: false,
      shiftKey: false,
      which: 27,
      charCode: 0,
      keyCode: 27,
    }));
  document.dispatchEvent(
    new KeyboardEvent("keyup", {
      altKey: false,
      code: "Escape",
      ctrlKey: false,
      isComposing: false,
      key: "Escape",
      location: 0,
      metaKey: false,
      repeat: false,
      shiftKey: false,
      which: 27,
      charCode: 0,
      keyCode: 27,
    }));
});

const closeDialogButton = document.querySelector(`#close-dialog-button`);
closeDialogButton.addEventListener("click", () => {
  dialog.close();
});
<input id="input-1" type="text">

<button id="open-dialog-button">Open Dialog</button>

<dialog>
  <div id="dialog-body">
    <p>Press the escape key to close the dialog box.</p>
    <input id="input-2" type="text">
    <button id="synthetic-escape-button">Press Synthetic Escape</button>
    <button id="close-dialog-button">Close Dialog</button>
  </div>
</dialog>

  1. 如果您点击“打开对话框”按钮,然后按下 escape 键,对话框将会关闭。
  2. 如果您点击“打开对话框”按钮,然后点击“按下合成的Escape”按钮,对话框不会关闭。

无论哪种方式,键盘事件监听器都会触发。为什么在情况 #1 中 <dialog> 关闭了,而在情况 #2 中却没有关闭?我该如何让合成的 escape 键关闭 <dialog>,就像情况 #1 一样?


为了背景,这是我正在编写的一个自动化测试,但我正在寻找纯粹的JS解决方案。我在Windows 10上使用Firefox浏览器。
更新:我刚刚了解到TestCafe的最新版本(v3)默认使用本机事件,但只适用于某些浏览器。Firefox不是其中之一。通过将我的测试切换到Google Chrome,我现在可以自动化模拟用户按下Esc键。
我只是出于信息目的说这个;我仍然希望得到对我原始问题的答案。希望这能为将来的某人节省很多时间。

1
你在使用什么测试框架?Cypress吗?如果是的话,你可能想要使用cy.type('{esc}')或者使用类似于https://github.com/dmtrKovalenko/cypress-real-events的库。 - undefined
1
好问题。我刚好在使用testcafe。起初,我打算接受testcafe的答案,但我决定了解如何在原生JS中实现这一点。这样,我就可以了解为什么它不起作用;通过理解这一点,也许可以帮助我理解涉及不同元素的未来场景。 - undefined
1个回答

1
对话框在#1(按下Esc键)时关闭,但在#2(模拟点击Esc按钮)时不关闭,因为可能存在“事件分发的差异”。
  1. 用户与键盘交互,
  2. 浏览器接收到“keydown”事件,
  3. 随后是“keyup”事件,
  4. 触发默认行为以关闭对话框。
但是,当您使用document.dispatchEvent()来分派一个“合成”事件时,它不会触发该事件的默认行为。事件监听器会触发,但对话框不会关闭,因为默认行为没有执行。
要使用合成的Esc键事件关闭对话框,您可以通过在Esc键的事件监听器中手动关闭对话框来处理该事件。
const syntheticEscapeButton = document.querySelector(`#synthetic-escape-button`);
syntheticEscapeButton.addEventListener("click", () => {
  handleEscapeKey(); // Call a custom function to handle the escape key event
});

function handleEscapeKey() {
  dialog.close(); // Manually close the dailog
}

通过在事件处理程序中直接调用dialog.close()方法,您可以模拟使用合成的逃逸键事件关闭对话框的行为,类似于场景#1。


1
谢谢!问题是,closeDialogButton 已经实现了这个功能。我已经有一个自动化测试命名为“当用户点击关闭按钮时,模态框关闭”。我正在尝试实现一个新的测试命名为“当用户按下 Esc 键时,模态框关闭”。对我来说,直接调用 dialog.close(); 感觉不太能测试到这一点。 - undefined
顺便问一下,你有没有想法为什么document.dispatchEvent()不会触发该事件的默认行为?这是<dialog>的一个奇怪边缘情况,还是合成事件与用户事件之间存在许多不同的例子? - undefined
我试着研究了一下,不确定是什么原因导致的。可能是一个特殊情况。 - undefined
哦,我想详细说明一下我的第一个评论。测试中的“当用户按下Esc键时,模态框关闭”可能听起来有点过分,或者可能听起来像是在测试浏览器而不是我的代码,但我不同意:将来我可能会将这个<dialog>实现替换为<div>,如果那一天到来,我希望按下Esc键的功能保持一致。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接