Reputation: 3138
NOTE So I actually managed to solve this issue whilst writing out the problem, but thought I would post anyway in case it helps anyone else out (a brief search on Stack Overflow indicated that no one else seemed to have asked quite the same question)
So I have a form with a web component that is handled via JavaScript, like so:
<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>
With my web component, I want to be able to 'submit' the form when tabbed to the web component and pressing 'enter', such that I have the following handler within the web component:
class WebComponent extends HTMLElement {
constructor() {
...
let form = this.closest('form');
this.addEventListener('keydown', (e) => {
if(form && e.code === 'Enter') form.submit();
})
}
}
The problem I have got is that form.submit()
submits the form, but doesn't fire an event that gets monitored, causing the form
event handler to not run and the form to simply post without any JS validation. My first thoughts for getting the form to work is to change the form submit function to a named function, something like:
form.addEventListener('submit', handleFormSubmission);
myComponent.addEventListener('keydown', (e) => {
if(e.code === 'Enter') handleFormSubmission();
})
But the problem here is that the web component cannot then necessarily be used for multiple forms, or would then need to have an event handler added to it each time it is called to ensure that it corresponds with the function name associated with the form. This would have to be done outside the component, and really reduce the readability.
The other option I have considered is to search the form for an input[type=submit]
or button[type=submit]
and trigger a click on that, but that seems somewhat clunky to me.
After some searching, I came across dispatchEvent
, which fires a submit
event as opposed to simply firing submit()
, like so:
class WebComponent extends HTMLElement {
constructor() {
...
let form = this.closest('form');
this.addEventListener('keydown', (e) => {
if(form && e.code === 'Enter') form.dispatchEvent(new Event('submit'));
})
}
}
And by now I am very close - the submit
handler, and the console.log
fires, but the e.preventDefault()
is not respected, meaning that if my JS validation wants to terminate the submission for any reason, it can't... By default.
A bit more searching (i.e. actually bothering to read the spec, rather than just plough ahead full-throttle and throwing mud at the wall to see what sticks) I realised that the Event
constructor takes an optional object for settings, such that you can have (e.g.) new Event('submit', {bubbles: true, cancelable: true})
. Finally, it works:
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}));
})
}
}
Upvotes: 1
Views: 132
Reputation: 3138
Just to clarify, the answer is to use dispatchEvent
to create a new Event
and set cancelable
to true
instead of .submit()
e.g.
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}));
})
}
}
Upvotes: 2