I just ran into an interesting situation where I have a submit <button>
inside the Shadow DOM of a native custom element that is placed inside a <form>
.
<form id="one" action="" method="get">
<s-button>Select</s-button>
#shadow-root
<button>...</button>
<button>Outside</button>
</form>
I also have a <button>
as a direct child of the <form>
.
The child <button>
causes the form to submit.
But the <button>
in the shadow-root does not.
In a way I guess this makes sense. But has anyone figured out a way to tell the shadow-root <button>
to work correctly with the <form>
or is this something I will have to handle through JS?
I know click events are blocked at the Shadow DOM layer, but I am surprised that there is no way to allow the button to still be a part of the form, something that can be set up through an attribute or a property.
Sure I can capture the click event and then send a new one from this
but that does not do the same thing since my event will no longer be user generated and there are a huge set of rules associated with that.
A button triggers a submit Event (on the FORM element)
Since Events can not pass the shadow DOM boundary (do not bubble up into the parent DOM)
I presume that is why a shadowDOM button (dispatching a submit
event) is not received by the FORM element.
Requires Supersharps workaround with a hidden button in the light DOM (which then dispatches a submit
event in the parent DOM)
Or (starting from light DOM) you find the (parent) FORM tag and dispatch a submit event yourself:
this.closest('FORM').dispatchEvent(new Event('submit'))
Follow the experts on shadowDOM and FORMs at: https://github.com/w3c/webcomponents/issues/187
customElements.define( 'my-button', class extends HTMLElement {
connectedCallback() {
this.attachShadow({mode:'open'}).innerHTML=`<button>Button In Shadow DOM</button>`
this.onclick = _ => this.closest('FORM').dispatchEvent(new Event('submit'))
}
})
<form onsubmit="return console.log('submit Event occured')">
<my-button></my-button>
<button>button in Document DOM</button>
</form>
If the FORM is not a direct ancestor, you can find it with something like: How to reference to a method in parent component from child component with vanilla JS Web Components? (Not any framework or Library)
You'll have to handle it through Javascript anyway.
A simple solution is to add a (masked) <button>
in the light DOM, and transfer the click
event to it.
customElements.define( 's-button', class extends HTMLElement {
connectedCallback() {
this.attachShadow( {mode: 'open'})
.innerHTML = `<button>In Shadow</button>`
var submit = this.appendChild( document.createElement( 'button' ) )
this.onclick = () => submit.click()
}
} )
<form onsubmit="console.log('submitted');return false">
<s-button>Select</s-button>
<button>Outside</button>
</form>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With