必须用原生标签加is属性,如<canvas is="render-area">且注册时指定{extends:"canvas"},否则无法获得原生Canvas特性;is值须与define首参完全一致,不可省略extends,不支持运行时切换,继承内置类constructor中必须调用super()。
is 属性,不能直接写自定义标签名浏览器解析 HTML 时,<render-area></render-area> 永远是 HTMLUnknownElement 实例,哪怕你注册了 class RenderArea extends HTMLCanvasElement。它没有 width、没有 getContext()、不能被表单收集、屏幕阅读器也认不出它是 canvas——因为 DOM 构建阶段就决定了它的原型链,而这个过程只认原生标签名。
正确姿势只有一种:<canvas is="render-area"></canvas>,且前提是注册时明确声明 { extends: "canvas" }:
class RenderArea extends HTMLCanvasElement { constructor() { super(); // 必须调用 }}customElements.define("render-area", RenderArea, { extends: "canvas" });
is 值("render-area")必须与 customElements.define() 第一个参数完全一致(大小写敏感)extends 配置项不可省略;漏掉它,is 属性彻底失效<canvas is="render-area"> 和 <button is="loading-button"> 是合法的;<render-area> 或 <canvas data-is="render-area"> 全部无效is 属性不支持运行时切换,改了也没用你不能在元素挂载后执行 el.setAttribute("is", "span-blue") 来“切换行为”。HTML 规范白纸黑字写着:元素创建后,is 值仅用于初始化,后续修改不会触发类替换或原型重绑定。
现象很典型:改完 is,el.constructor.name 还是原来的,el instanceof HTMLSpanElement 仍为 false,所有新增方法/属性访问都报 undefined。
立即学习“前端免费学习笔记(深入)”;
attributeChangedCallback 监听 is 并自动升级——它不会生效constructor 移到 connectedCallback,靠属性(如 mode="blue")或数据驱动样式/功能分支super() 是硬性要求,漏掉会出错扩展 HTMLButtonElement、HTMLInputElement 等内置类时,constructor 中不调用 super() 会导致构造失败,控制台报 Failed to construct 'HTMLButtonElement': Please use the 'new' operator 类错误。
这不是可选建议,而是 WebIDL 绑定层强制校验:内置元素构造器依赖父类完成底层对象初始化(比如关联表单、设置默认 ARIA role、初始化渲染上下文等)。
HTMLxxxElement 的类,constructor 第一行必须是 super()
super() 后才能访问 this.value、this.checked、this.width 等原生属性super({ capture: true })),目前仅部分现代浏览器支持,稳妥起见只用无参 super()
data-is 或 class 能替代 is
data-is="xxx"、class="my-button"、role="button" 全都不影响元素类型。它们只是普通字符串或语义提示,对原型链、属性集、事件模型、表单提交行为零影响。
比如你写 <button data-is="loading-button">,它仍是标准 HTMLButtonElement,不会调用你注册的 LoadingButton 类的任何生命周期方法,connectedCallback 根本不会触发。
is 是 HTML 规范中唯一具有“类型升级”语义的原生属性data- 属性仅用于存储数据,DOM 不会据此改变实例类型is 实现类似效果?只能用独立自定义元素(<loading-button>)+ 手动封装原生 <button>,但会丢失原生语义和表单集成能力extends 配置项,或者下意识写了自定义标签名却期待它有原生行为——这两点一旦出错,调试时所有属性访问都是 undefined,但控制台又不报错,非常隐蔽。