render-area 标签 width 为 undefined 是因为浏览器将其识别为 HTMLUnknownElement;正确写法是 <canvas is="render-area" width="400"></canvas>,且需满足 customElements.define("render-area", RenderArea, { extends: "canvas" })、is 值严格匹配、仅原生 is 属性有效三个条件。

为什么 render-area 标签写出来 width 一直是 undefined
因为你用了自定义标签名,浏览器根本没把它当 canvas。哪怕你注册了 class RenderArea extends HTMLCanvasElement,只要写的是 <render-area width="400"></render-area>,它在 DOM 里就是 HTMLUnknownElement 实例——没有 width、没有 getContext()、不能被表单收集、屏幕阅读器也认不出。
真正起作用的只有一种写法:<canvas is="render-area" width="400"></canvas>,且必须满足三个硬性条件:
-
customElements.define("render-area", RenderArea, { extends: "canvas" })中的extends配置项不能省,漏掉就彻底失效 -
is属性值("render-area")必须和define()第一个参数完全一致,大小写、连字符都不能错 - 不能用
data-is、class或其他属性模拟,只有原生is属性有语义升级能力
动态改 is 属性为什么没反应
HTML 规范明确写了:is 只在元素创建时生效,后续修改不触发类替换或原型重绑定。你执行 el.setAttribute("is", "span-blue") 后,el.constructor.name 还是原来的,el instanceof HTMLSpanElement 仍为 false,所有新增方法/属性访问都报 undefined。
真要实现“切换”效果,得靠数据驱动,而不是改 is:
立即学习“前端免费学习笔记(深入)”;
- 把样式、行为逻辑从
constructor挪到connectedCallback,靠普通属性(比如mode="blue")控制分支 - 如果必须换底层行为,只能手动销毁旧实例、新建目标类型元素、再迁移子节点——代价高,慎用
-
attributeChangedCallback监听is并调super()?不会生效,别白费劲
constructor 里不写 super() 会怎样
直接报错:Failed to construct 'HTMLButtonElement': Please use the 'new' operator。这不是警告,是 WebIDL 绑定层强制校验——内置元素构造器依赖父类完成底层初始化(比如关联表单、设置默认 disabled 状态、挂载事件代理等)。
所有继承自内置类的自定义元素都必须遵守:
-
class LoadingButton extends HTMLButtonElement→ 构造函数第一行必须是super() -
class WordCount extends HTMLParagraphElement→ 同样必须super() - 哪怕你什么也不做,漏掉这行,实例化就失败,控制台红字报错
服务端渲染或 innerHTML 里写 is 有用吗
服务端渲染写死 <button is="loading-button"></button> 是可以的,但前提是客户端 JS 必须提前注册该类,否则降级为普通 button,自定义逻辑全丢。
而用 innerHTML = "<canvas is='render-area'>" 是无效的——HTML 解析器在插入时不会触发 is 升级。动态创建定制化内置元素的唯一合规方式是:
document.createElement("canvas", { is: "render-area" })- 或者
document.createElement("button", { is: "loading-button" }) - 不能靠字符串拼接 +
innerHTML,也不能靠cloneNode()复制已升级的元素再改is
最易被忽略的一点:extends 的值必须是小写字符串("canvas"、"button"),不能写成 "Canvas" 或 "HTMLCanvasElement",否则注册失败且无提示。


















