表单组件必须用 attachshadow 而不是 class 封装,因为 bem 或 css 类无法阻止外部样式穿透,!important 也无效;shadow dom 是浏览器强制执行的隔离边界,使内部 dom、样式、事件完全解耦。

为什么表单组件必须用 attachShadow 而不是 class 封装
class 命名(如 .form-card__input)或 BEM 约定根本无法阻止外部样式穿透,!important 也无效;而 Shadow DOM 是浏览器强制执行的隔离边界,<input> 在 shadowRoot 内部的样式、事件、DOM 查询全部与主文档解耦。不走这一步,所谓“封装”只是自我安慰。
常见翻车点:
- 用
<div class="my-form"> + <code>innerHTML拼接结构 —— 外部input:focus { outline: 2px solid red }会污染所有内部输入框 - 在 light DOM 里监听
submit事件,却忘了 shadowRoot 内部的<form></form>不会冒泡到外层(除非显式dispatchEvent) - 给宿主元素设
tabindex="-1",导致delegatesFocus: true失效,点击卡片空白处焦点进不去 - 必须在
attachShadow({ mode: 'open', delegatesFocus: true })时声明,创建后无法修改 - shadowRoot 内至少有一个原生可聚焦元素:
<input>、<select></select>、<button></button>,或带tabindex="0"的容器 - 避免所有字段都设
tabindex="-1"或disabled,否则委托失败,焦点停在宿主元素上 - 若需控制入口点(比如点击卡片默认聚焦搜索框而非第一个
<input>),在connectedCallback中调用this.shadowRoot.querySelector('input.search').focus() - 在
attributeChangedCallback中监听value属性变化,并同步写入input.value和input.setAttribute('value', newValue) - 在
input的addEventListener('input', ...)里反向更新this._value和this.setAttribute('value', ...) - 对多字段组件(如
<date-range-picker></date-range-picker>),用FormData或自定义getFormData()方法聚合子字段值,别依赖遍历 light DOM - 在
connectedCallback或attributeChangedCallback中调用getComputedStyle(this).getPropertyValue('--theme-color') - 把结果赋给内部元素的
style.color或通过setAttribute('style', ...)注入 - 在 shadowRoot 的
<style></style>中写:host { font-family: inherit; color: inherit; },确保基础文本属性穿透 - 禁止用
shadowRoot.innerHTML = '<style>...</style>'注入样式 —— 这样生成的<style></style>不进 CSSOM,后续 JS 改textContent完全无效
如何让自定义表单组件支持 Tab 键连续导航
关键不是写一堆 tabindex,而是靠 delegatesFocus: true + 可聚焦子元素的组合生效。它只负责“第一次进入”,后续 Tab 顺序由浏览器按 DOM 顺序自动处理。
实操要点:
表单状态同步:为什么不能只改 this._value
customElements.define() 不提供响应式绑定,this._value = 'xxx' 不会自动更新 shadowRoot 里的 <input value="...">。用户看到的永远是旧值,提交拿到的也是旧值。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
正确做法是把状态变更和 DOM 更新绑死:
样式继承与主题变量怎么透传进 Shadow DOM
Shadow DOM 默认不继承 font-family、color 等可继承属性,var(--theme-color) 也不会自动生效——它不是“没加载”,是根本不在作用域内。
必须手动桥接:
复杂点在于:主题变量可能动态变更,但 getComputedStyle 不会自动监听。得配合 MutationObserver 监听宿主元素 class 变化,或约定上游通过属性变更触发重读。这点容易被忽略,一动不动的主题色背后其实是手动同步逻辑。










