我可以在 Shadow DOM 中获得一个按钮来提交不在 Shadow DOM 中的表单吗?

Posted

技术标签:

【中文标题】我可以在 Shadow DOM 中获得一个按钮来提交不在 Shadow DOM 中的表单吗?【英文标题】:Can I get a button in Shadow DOM to submit a form not in Shadow DOM? 【发布时间】:2019-06-19 14:13:51 【问题描述】:

我刚刚遇到了一个有趣的情况,我在放置在 <form> 内的原生自定义元素的 Shadow DOM 内提交了 <button>

  <form id="one" action="" method="get">
    <s-button>Select</s-button>
      #shadow-root
        <button>...</button>
    <button>Outside</button>
  </form>

我还有一个&lt;button&gt; 作为&lt;form&gt; 的直系子代。

&lt;button&gt; 导致表单提交。

但 shadow-root 中的 &lt;button&gt; 没有。

在某种程度上,我想这是有道理的。但是有没有人想出一种方法来告诉 shadow-root &lt;button&gt;&lt;form&gt; 正常工作,或者这是我必须通过 JS 处理的事情?

我知道点击事件在 Shadow DOM 层被阻止,但令我惊讶的是,没有办法让按钮仍然是表单的一部分,可以通过属性或属性进行设置。

当然,我可以捕获点击事件,然后从this 发送一个新事件,但这不会做同样的事情,因为我的事件将不再是用户生成的,并且有大量与之相关的规则。

【问题讨论】:

他们在 V2 中处理的 shadowDOM 和表单存在更多问题。请阅读/关注:github.com/w3c/webcomponents/issues/187 并且(从几年前开始)Supersharps 对将 shadowDOM 输入复制到主 DOM 的回答:***.com/questions/38623176/… 点击/提交时:来自the submit Event documentation:“提交事件仅在用户单击表单中的提交按钮[..]时触发。调用form.submit时不会引发该事件() 方法直接。" 【参考方案1】:

按钮触发submit Event(在 FORM 元素上)

由于事件不能通过影子 DOM 边界(不要冒泡进入父 DOM)

我想这就是为什么 FORM 元素没有收到 shadowDOM 按钮(发送 submit 事件)的原因。

需要 Supersharps 解决方法,在 light DOM 中使用隐藏按钮(然后在父 DOM 中调度 submit 事件)

或者(从轻量级 DOM 开始)您找到(父)FORM 标签并自己发送提交事件:

this.closest('FORM').dispatchEvent(new Event('submit'))


关注 shadowDOM 和 FORM 方面的专家: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>

嵌套 shadowDOMs

如果 FORM 不是直接祖先,您可以通过以下方式找到它:How to reference to a method in parent component from child component with vanilla JS Web Components? (Not any framework or Library)

【讨论】:

【参考方案2】:

无论如何,您都必须通过 javascript 来处理它。

一个简单的解决方案是在 light DOM 中添加一个(屏蔽的)&lt;button&gt;,并将click 事件传递给它。

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>

【讨论】:

【参考方案3】:

你可以做的不是完全ShadowDOM 中的一个按钮,如果你的button[type=submit] 已经被插入到ShadowDOM 中。所以如果你有类似的东西:

<some-component>
<button type="submit" slot="buttonSlot"></button>
</some-component>

该按钮可用于触发您的表单。 该按钮位于 light DOM 中,但可以通过插槽在组件内轻松处理。它还将毫无问题地保留所有适当的键盘、单击、焦点等事件。

要最小化轻量级 DOM html,您甚至不需要将其设置为 type=submit,您可以在组件中设置它,它仍将被视为轻量级 DOM 中任何父表单的提交按钮。

奖励(或者可能会根据您的看法而带来麻烦),它将保留页面上其他按钮的样式(除非您在组件中更改它)。

【讨论】:

以上是关于我可以在 Shadow DOM 中获得一个按钮来提交不在 Shadow DOM 中的表单吗?的主要内容,如果未能解决你的问题,请参考以下文章

shadow-dom浅析

将全局样式应用到 Shadow DOM 的正确方法

shadow-dom 浅析

神秘的 shadow-dom 浅析

在 iframe 的文档和/或 shadow-root 中注入 html dom

Firefox:shadow-DOM 兼容性