如何确保 JavaScript .submit() 遵守附加到表单的事件处理程序

How to ensure a JavaScript .submit() respects event handlers attached to the form

注意 所以我实际上在写出问题的同时设法解决了这个问题,但我想无论如何我都会 post 以防它帮助其他人(对 Stack Overflow 的简短搜索表明没有其他人似乎问了完全相同的问题)


所以我有一个带有 Web 组件的表单,该表单通过 JavaScript 处理,如下所示:

<form id="myform">
  <input type="text" value="example" tabindex="1" />
  <web-component tabindex="2"></web-component>
  <input type="submit" tabindex="3" />
</form>

<script>
let form = document.getElementById('myform');

form.addEventListener('submit' (e) => {
  e.preventDefault();

  console.log('Form is posting');

  // Handle form stuff here
})
</script>

对于我的 Web 组件,我希望能够 'submit' 将表单切换到 Web 组件并按下 'enter',这样我在 Web 组件中就有了以下处理程序:

class WebComponent extends HTMLElement {
  constructor() {
    ...

    let form = this.closest('form');

    this.addEventListener('keydown', (e) => {
      if(form && e.code === 'Enter') form.submit();
    })
  }
}

我遇到的问题是 form.submit() 提交了表单,但没有触发被监视的事件,导致 form 事件处理程序不 运行 并且形式简单地 post 没有任何 JS 验证。让表单工作的第一个想法是将表单提交函数更改为命名函数,例如:

form.addEventListener('submit', handleFormSubmission);

myComponent.addEventListener('keydown', (e) => {
  if(e.code === 'Enter') handleFormSubmission();
})

但这里的问题是,web组件不一定能用于多个表单,或者每次调用时都需要添加一个事件处理程序,以确保它与关联的函数名称相对应与表格。这将不得不在组件之外完成,并且确实降低了可读性。

我考虑过的另一个选择是在表单中搜索 input[type=submit]button[type=submit] 并点击它,但这对我来说似乎有些笨拙。

经过一些搜索,我发现了 dispatchEvent,它触发了一个 submit 事件,而不是简单地触发 submit(),就像这样:

class WebComponent extends HTMLElement {
  constructor() {
    ...

    let form = this.closest('form');

    this.addEventListener('keydown', (e) => {
      if(form && e.code === 'Enter') form.dispatchEvent(new Event('submit'));
    })
  }
}

现在我非常 关闭 - submit 处理程序,console.log 触发,但 e.preventDefault() 未得到尊重,这意味着如果我的 JS 验证出于任何原因想要终止提交,它不能...默认情况下。

多一点搜索(即实际上费心阅读规范,而不是只是全速前进并向墙上扔泥巴看看有什么粘住)我意识到 Event 构造函数采用可选设置对象,这样你就可以拥有(例如)new Event('submit', {bubbles: true, cancelable: true})。终于成功了:

let form = document.getElementById('myform');

form.addEventListener('submit' (e) => {
  e.preventDefault();

  console.log('Form is posting');

  // Handle form stuff here
});

class WebComponent extends HTMLElement {
  constructor() {
    ...

    let form = this.closest('form');

    this.addEventListener('keydown', (e) => {
      if(form && e.code === 'Enter') form.dispatchEvent(new Event('submit', {cancelable: true}));
    })
  }
}

澄清一下,答案是使用 dispatchEvent 创建一个新的 Event 并将 cancelable 设置为 true 而不是 .submit()

例如

let form = document.getElementById('myform');

form.addEventListener('submit' (e) => {
  e.preventDefault();

  console.log('Form is posting');

  // Handle form stuff here
});

class WebComponent extends HTMLElement {
  constructor() {
    ...

    let form = this.closest('form');

    this.addEventListener('keydown', (e) => {
      if(form && e.code === 'Enter') form.dispatchEvent(new Event('submit', {cancelable: true}));
    })
  }
}