Chessly项目中如何区分常规IMG与自定义内置IMG元素?
<img>元素的识别与类型追踪问题 嘿,我来帮你拆解下你遇到的这几个自定义内置元素的问题~
1. 创建自定义内置<img>时is属性的困惑
你用document.createElement("IMG", { is: "white-queen" })创建元素时,DOM上可能不会直接显示is属性,但其实这个元素已经被正确关联到了你的自定义类上!你可以通过元素的**is属性(不是HTML属性)**来获取它的自定义类型:
const queen = document.createElement("img", { is: "white-queen" }); console.log(queen.is); // 输出 "white-queen"
手动设置is HTML属性(setAttribute("is", "..."))不会改变元素的实际类——因为自定义内置元素一旦被升级(比如插入DOM时),它的类就固定了,修改is属性只是改了HTML标记,不会触发重新关联自定义类。
2. 区分常规<img>和自定义内置<img>
你之前的原型链判断方法有局限性,无法覆盖所有情况。这里有两个更可靠的方案:
方案一:利用element.is和自定义元素注册表
function isCustomBuiltInImg(element) { if (!(element instanceof HTMLImageElement)) return false; // 检查element.is是否存在,且对应已注册的自定义元素类 return !!element.is && !!customElements.get(element.is); }
这个方法直接利用了自定义内置元素的原生特性,既准确又清晰,还能区分自治自定义元素(自治元素的is属性为空)。
方案二:检查原型链(兼容旧环境)
如果需要兼容不支持element.is的环境,可以通过原型链判断:
function isCustomBuiltInImg(element) { if (!(element instanceof HTMLImageElement)) return false; // 自定义内置元素的原型链是:自定义类原型 → HTMLImageElement原型 → ... const proto = Object.getPrototypeOf(element); return proto !== HTMLImageElement.prototype; }
这个方法的逻辑是:常规<img>的直接原型就是HTMLImageElement.prototype,而自定义内置<img>的直接原型是你自定义类的原型。
3. 修改is属性后追踪原类型
正如你发现的,修改is属性不会改变元素的实际类(它仍然属于最初注册的自定义类),所以要追踪原类型,你需要自己存储这个信息:
- 在自定义元素的
connectedCallback中,用自定义数据属性记录初始类型:
class WhitePawn extends HTMLImageElement { connectedCallback() { // 仅在第一次连接时记录原类型 if (!this.dataset.originalIs) { this.dataset.originalIs = this.is; } // 你的初始化逻辑 } } customElements.define('white-pawn', WhitePawn, { extends: 'img' });
之后不管你怎么修改is属性,都可以通过element.dataset.originalIs拿到它最初的类型:
const pawn = document.querySelector('img[is="white-pawn"]'); pawn.setAttribute('is', 'white-queen'); console.log(pawn.dataset.originalIs); // 输出 "white-pawn" // 同时元素的实际类还是WhitePawn console.log(pawn.constructor.name); // 输出 "WhitePawn"
如果想要真正把白兵“升级”成皇后,你需要创建一个新的white-queen元素,把原元素的属性、内容迁移过去,再替换掉原元素——因为自定义元素的类一旦确定就无法更改。
内容的提问来源于stack exchange,提问作者Danny 'Quest' Engelman

