你写了一个 自定义元素。五颗可以点击的星星,一个顺眼的悬停状态,一个 value 属性。你把它放进注册表单里,紧挨着那些真正的输入框。它看起来和周围的一切一模一样。
然后你提交表单。FormData 回来了,带着邮箱、密码、复选框——唯独没有评分。不是空值,是压根不存在。对
来说,你这个自定义元素从来没出现过。
![]()
这不是 bug,是平台规则。
表单只认一份固定名单
一个 只会从它认得的表单控件上收集值。这份名单是写死在平台里的:、、</strong>、,以及另外少数几个。
自定义元素不在名单上。不管它渲染得多像那么回事,不管你把它的属性起成什么名字,都不在。它可以有 name 属性,可以有 value getter,表单依然会像路过一个
一样路过它——因为就表单提交这件事而言,它确实就是一个 div。
你从 那里白拿的其余表单契约,也是同样的下场:
- 不会聚焦它
- required 不会阻止它提交
- :invalid 不会给它上样式
- form.reset() 不会清空它
这些全都没有接上。平台从来没有为自定义元素留过一个可以主动接入的机制。
最直觉的做法:藏一个镜像
显而易见的解法是加一个隐藏的镜像输入框。一个 ,配一个 ,然后监听星星的变化,把值同步过去。
这能跑通,前提是你说的"跑通"是指 FormData 里终于有值了。代价是你要额外维护一个元素,它存在的唯一意义就是:在第一个元素是假的地方,它是真的。
评分能变的每一个地方——点星星、键盘快捷键、预设按钮——都得记得同步这个镜像,否则两边会悄无声息地漂移开。
required 看起来是下一个要打的勾
于是有人把 required 加到了那个隐藏输入框上。它什么也不做。
被明确排除在约束校验之外,规范里的说法是"被禁止参与约束校验"。所以一个 required 的隐藏字段永远被视为有效,句号。
真正上线的是手写的检查,挂在提交处理器上:如果镜像的值为空,就阻止默认行为。这就是 required,用应用层代码重新实现了一遍,没有 :invalid 样式,也没有原生的"请填写此项"提示——因为那个逻辑本可以原生存在的地方,从来就不在候选名单里。
form.reset() 清掉了隐藏框,却没清掉星星
所以这又是一个要手写的监听器。你缺失的每一个原生行为,都会被重新实现成一个独立的事件监听器,挂在独立的元素上,还得和用户真正看到的那个元素保持同步。
这就是自定义元素在表单里的处境:渲染上它赢了,契约上它是个局外人。你得到的每一分原生能力,都得自己再挣一遍。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.