delegatesfocus 必须在 attachshadow 时通过参数声明,不可后续修改;启用后需确保 shadow 内有且仅有一个自然可聚焦元素,并手动同步宿主聚焦状态以支持 css 反馈和无障碍访问。

delegatesFocus 必须在 attachShadow 时声明,不能补开
很多人写完组件才发现焦点进不去,回头加 shadowRoot.delegatesFocus = true,这完全无效。该属性是 Shadow Root 初始化时的只读配置,一旦 attachShadow() 执行完毕就无法修改。
正确写法只有这一种:this.attachShadow({ mode: 'open', delegatesFocus: true })。漏掉这个参数,后续所有 focus 管理逻辑(比如监听内部 input 的 focus 事件)都只是打补丁,无法修复原生 Tab 顺序断裂问题。
- 若用 ReactShadow 或其他封装库,确认其 API 是否透传
delegatesFocus参数,有些默认为false - 自定义元素中,必须在
constructor或connectedCallback早期调用attachShadow,避免延迟创建导致状态不一致 -
mode: 'closed'下无法访问shadowRoot,但delegatesFocus仍生效——只是你没法手动调试或注入测试逻辑
内部可聚焦元素必须唯一且显式可聚焦
启用 delegatesFocus: true 后,浏览器会自动把焦点委托给 shadow 内第一个“可聚焦元素”。但它不是按 DOM 顺序找,而是按“tabindex 可达性”规则:优先找 tabindex="0" 或原生可聚焦标签(input、button、select 等),忽略 tabindex="-1" 和禁用态元素。
常见错误是同时放了 <input> 和 <button tabindex="0"></button>,结果每次点击外部区域焦点都落到按钮上,用户根本输不了内容。
通过 `dcc-mcp-cli` 命令行工具控制实时 DCC 主机(Maya、Blender、Houdini、Photoshop、3ds Max 以及自定义的 Studio 工具)。 针对 ClawHub、OpenClaw、Cursor、Claude 和支持 shell 的 Agent 主机:验证网关健康状态、列出已注册的 DCC 实例、搜索工具、检查 Schema、调用工具——全程无需直接编写或发送 MCP 请求。 若系统中未安装 `dcc-mcp-cli`,需先征得用户同意,再从 GitHub
- 确保 shadow 内有且仅有一个“主交互元素”具备自然可聚焦能力(如未禁用的
input) - 避免给非主控件设
tabindex="0";若需键盘操作但不参与主流程,用tabindex="-1"并手动.focus() - 不要依赖
autofocus:多个autofocus元素行为未定义,且首次加载后焦点不会自动回退
宿主元素需同步内部聚焦状态,否则视觉反馈失效
delegatesFocus 解决了“能不能进”的问题,但没解决“进没进”的反馈。用户点击组件空白区,焦点进了内部 input,但宿主元素本身没任何状态变化,CSS 中的 :host(:focus) 不会触发,也无法通过 :focus-within 可靠捕获(部分旧版 Safari 不支持)。
必须手动桥接:在 shadow 内部监听 focus 和 blur,再更新宿主属性。
- 在 shadow 中绑定:
input.addEventListener('focus', () => this.hostElement.setAttribute('focused', '')) - 对应移除:
input.addEventListener('blur', () => this.hostElement.removeAttribute('focused')) - CSS 写成:
:host([focused]) { outline: 2px solid #007aff; },而非依赖伪类 - 别用
focusin/focusout:它们不穿透 shadow boundary,宿主里监听不到
复杂组件(下拉/模态框)必须开启 delegatesFocus,否则键盘用户直接卡死
带浮层的组件(如 <custom-select></custom-select>、<date-picker></date-picker>)最容易被忽略这一点。用户按 Tab 进入组件后,焦点停在 trigger 按钮上,但按下空格/Enter 展开面板时,新内容在 shadow 内动态插入——若没启用 delegatesFocus,键盘用户无法用 Tab 进入选项列表,只能靠鼠标点,违反 WCAG 2.1 的“键盘可操作”要求。
更隐蔽的问题是:某些组件在展开后会主动 input.focus(),但这破坏了浏览器原生焦点链,导致 Shift+Tab 无法正确回退到上一个表单控件。
- 只要 shadow 内存在需要键盘操作的子区域(菜单项、日期格子、滑块 thumb),就必须启用
delegatesFocus - 动态插入内容后,确保新插入的可聚焦元素是当前 shadow 内唯一满足条件的候选者
- 如果组件结构复杂到无法保证唯一性(比如多 tab 面板同时存在 input),应改用
tabindex="-1"+ 显式.focus()控制,但需自行维护焦点顺序
delegatesFocus 就万事大吉,结果测试时发现键盘用户看不见焦点在哪,只能靠反复 Tab 猜位置。










