自定义元素语义需手动构建:命名须含连字符(如<date-picker>),继承原生元素须用is属性(如<input is="calendar-input">),并主动设置ARIA、调用attachInternals()、监听attributeChangedCallback以同步属性。

自定义元素本身不增强语义,它只是提供封装能力;真正提升语义表达力的,是**你如何命名、如何组合、如何暴露属性与事件**——浏览器不会因为你写了 <user-avatar> 就自动理解“这是用户头像”,但辅助技术(如屏幕阅读器)能通过你显式设置的 role、aria-label 和原生语义结构识别意图。
自定义标签名必须含连字符,且不能是已有HTML标签的变体
浏览器靠连字符(-)区分原生标签和自定义元素。写 <mybutton> 会被当作文本节点处理,<my-button> 才会被 customElements 正确识别并升级为实例。
-
<date-picker>✅ 合法,语义清晰,可读性强 -
<datepicker>❌ 不触发升级,DOM 中就是个未知元素 -
<button-ext>⚠️ 虽合法,但易与原生<button>混淆,且无法复用HTMLButtonElement的 form 关联行为(见下条) - 禁止使用
is属性配合非原生标签名,比如<date-picker is="date-picker">—— 浏览器直接忽略自定义类
扩展现有语义元素必须用 is 属性,否则 width/checked/value 全失效
你想让 <input type="date"> 支持日历弹窗?不能写 <calendar-input>,得继承 HTMLInputElement 并注册为 extends: 'input',然后在 HTML 中写:<input is="calendar-input" type="date">。
- 漏掉
is属性 → 浏览器按普通<input>解析,但你的类没被实例化,所有自定义逻辑不执行 -
is值大小写/连字符必须与customElements.define()第一个参数完全一致,"CalendarInput"和"calendar-input"是两个不同元素 - Chrome 100+ 已限制对大多数内置表单控件(
input、select、textarea)的继承支持,实际项目中更推荐自治型方案 +attachInternals()
语义不是靠标签名自动获得的,得手动补全 ARIA 和表单参与能力
写了个 <status-badge>,只渲染一个带颜色的 span,屏幕阅读器根本不知道它代表“成功”还是“错误”。要让它真正可访问,必须:
立即学习“前端免费学习笔记(深入)”;
- 在构造函数里调用
this.attachInternals()(如果声明了formAssociated: true) - 根据状态动态设置
this.setAttribute('role', 'status')或this.setAttribute('aria-live', 'polite') - 若需参与表单提交(比如作为隐藏字段),必须在
connectedCallback中监听子元素变化,并调用this.internals.setFormValue() - 避免把
role="button"加在<div>上却不处理Enter/Space键,这反而破坏语义一致性
最容易被忽略的一点:自定义元素的属性映射不是自动的。你写了 <user-avatar email="a@b.com">,但类里不会自动同步到 this.email,必须手动监听 attributeChangedCallback 并解析 email 属性值——否则语义信息就卡在 HTML 字符串里,进不了 JS 逻辑。



















