自定义元素默认不可访问,需在connectedcallback中补全role、aria属性、tabindex和键盘事件。constructor中dom未挂载,无法安全操作;shadow dom内id引用需用this.shadowroot.getelementbyid();attributechangedcallback需显式监听属性并处理字符串值。

自定义元素默认对屏幕阅读器不可见、不可聚焦、不可操作,必须手动补全语义、焦点控制和键盘行为,否则就是个“空盒子”。
为什么 customElements.define 创建的组件默认不可访问
浏览器不会给 <my-button></my-button> 这类标签自动分配 role 或隐式语义。它在辅助技术中就是未定义类型,读屏器只报“自定义元素”,不朗读内容,不响应 Tab,也不支持 Enter/Space 激活。
- 常见错误:用
<my-toggle></my-toggle>替代<input type="checkbox">,但没设role="switch"和aria-checked - 组件内文本可读,但焦点无法到达——忘了加
tabindex="0"或没处理键盘事件 - Shadow DOM 中的 ID 引用(如
aria-labelledby)若未用this.shadowRoot.getElementById()查找,会失效
如何给自定义元素补全无障碍四要素
不能只写 role="button" 就完事。role 只是告诉读屏器“这是什么”,用户还得能聚焦、操作、感知状态、理解用途。
- 用
role明确类型(如role="combobox"、role="slider") - 用
aria-labelledby(优先)或aria-label提供可访问名称,避免硬编码文本 - 用
aria-*状态属性反映实时变化(如aria-expanded="true"、aria-checked="false") - 用
tabindex="0"+keydown监听Enter/Space/ArrowDown等事件实现可聚焦与可操作
反例:<my-slider role="slider"></my-slider> 缺少 aria-valuenow、aria-valuemin、aria-valuemax,读屏器只知“这是滑块”,不知当前值、范围、是否可调。
为什么必须在 connectedCallback 里初始化无障碍逻辑
constructor 中 DOM 尚未挂载,此时无法安全操作子节点、添加事件监听器或读取 this.innerHTML。所有无障碍相关操作(设 tabindex、绑定 keydown、注入 aria- 属性)都必须等到元素真正插入文档后执行。
- 错误:在
constructor里调用this.setAttribute('tabindex', '0'),可能被后续渲染覆盖 - 正确:在
connectedCallback中设置初始tabindex、查询 Shadow DOM 内控件、绑定键盘事件 - 若用 Shadow DOM,需确保
aria-labelledby引用的 ID 在this.shadowRoot内存在,且不是全局 document 上的 ID
attributeChangedCallback 里怎么同步布尔属性状态
这个回调只响应 setAttribute、removeAttribute 或 HTML 初始属性(如 <my-toggle checked></my-toggle>),对 JS 属性赋值(如 el.checked = true)完全无感。
- 必须在
static get observedAttributes()显式声明要监听的属性,漏写就收不到回调 -
newValue是字符串,checked这类布尔属性初始值可能是null或"true",别直接拿oldValue === newValue判断 - JS 属性赋值需手动同步到内部控件(如
input.checked = this.hasAttribute('checked'))和 ARIA 状态(如this.setAttribute('aria-checked', this.hasAttribute('checked')))
最易忽略的一点:Shadow DOM 内部的焦点管理、ID 引用、键盘事件委托,全部依赖于 connectedCallback 的时机和作用域——早一毫秒或晚一毫秒,都会导致 ARIA 属性不生效、焦点卡死、读屏器静默。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











