javascript - 如果通过 html 属性订阅,为什么我的 Web 组件的 CustomEvent 事件处理程序没有被调用?
问题描述
Windows 10 x64,谷歌浏览器 v87.0.4280.66。
如果我通过 JavaScript 订阅组件的事件,那么我的事件处理程序可以正常工作。但是,如果我尝试通过 HTML 文件中的属性来做同样的事情,那么我的事件处理程序将不起作用。
所以,它工作正常:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom component</title>
</head>
<body>
<custom-counter id="counter" counter="5"></custom-counter>
<script src="custom-counter.js"></script>
</body>
</html>
代码:
// custom-counter.js
class CustomCounter extends HTMLElement {
static get htmlTagName() { return "custom-counter"; }
constructor() {
super();
}
#shadow;
#render() {
console.log("#render called");
if(this.#shadow) {
this.#shadow.getElementById("counter").textContent = `* ${this.counter.toString()} *`;
}
}
get counter() {
const attName = "counter";
return this.hasAttribute(attName) ? parseInt(this.getAttribute(attName)) : 0;
}
set counter(value) {
const attName = "counter";
this.setAttribute(attName, value);
this.dispatchEvent(new CustomEvent("counterchanged", {detail: { value: this.counter}}));
}
connectedCallback() {
this.#shadow = this.attachShadow({mode:"closed"});
this.#shadow.innerHTML = `<div><span id="counter">* ${this.counter} *</span></div>`;
const element = this.#shadow.getElementById("counter");
element.style.background = "yellow";
element.addEventListener("click", event => ++this.counter);
}
attributeChangedCallback(attName, oldValue, newValue) {
console.log(`The '${attName}' attribute changed. oldValue: ${oldValue}. newValue: ${newValue}`);
this.#render();
}
static get observedAttributes() {
return ["counter", "counterchanged"];
}
}
customElements.whenDefined(CustomCounter.htmlTagName).then(
() => console.log(`'${CustomCounter.htmlTagName}' web component is defined.`));
if(!customElements.get(CustomCounter.htmlTagName)) {
customElements.define(CustomCounter.htmlTagName, CustomCounter);
}
// It works fine:
const counter = document.getElementById("counter");
counter.addEventListener("counterchanged", event => console.log(event.detail));
看起来像这样:
控制台输出(当我单击我的 Web 组件时):
好的。现在我想将事件注册从代码移动到 html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Custom component</title>
</head>
<body>
<custom-counter id="counter" counter="5" oncounterchanged="console.log(event.detail)"></custom-counter>
<script src="custom-counter.js"></script>
</body>
</html>
代码:
// custom-counter.js
class CustomCounter extends HTMLElement {
static get htmlTagName() { return "custom-counter"; }
constructor() {
super();
}
#shadow;
#render() {
console.log("#render called");
if(this.#shadow) {
this.#shadow.getElementById("counter").textContent = `* ${this.counter.toString()} *`;
}
}
get counter() {
const attName = "counter";
return this.hasAttribute(attName) ? parseInt(this.getAttribute(attName)) : 0;
}
set counter(value) {
const attName = "counter";
this.setAttribute(attName, value);
this.dispatchEvent(new CustomEvent("counterchanged", {detail: { value: this.counter}}));
}
connectedCallback() {
this.#shadow = this.attachShadow({mode:"closed"});
this.#shadow.innerHTML = `<div><span id="counter">* ${this.counter} *</span></div>`;
const element = this.#shadow.getElementById("counter");
element.style.background = "yellow";
element.addEventListener("click", event => ++this.counter);
}
attributeChangedCallback(attName, oldValue, newValue) {
console.log(`The '${attName}' attribute changed. oldValue: ${oldValue}. newValue: ${newValue}`);
this.#render();
}
static get observedAttributes() {
return ["counter", "counterchanged"];
}
}
customElements.whenDefined(CustomCounter.htmlTagName).then(
() => console.log(`'${CustomCounter.htmlTagName}' web component is defined.`));
if(!customElements.get(CustomCounter.htmlTagName)) {
customElements.define(CustomCounter.htmlTagName, CustomCounter);
}
// It works fine:
/* const counter = document.getElementById("counter");
counter.addEventListener("counterchanged", event => console.log(event.detail));
*/
但现在我的事件处理程序不起作用:
为什么会发生,我该如何解决?
解决方案
我自己解决了这个问题(查看connectedCallback
函数中的注释):
// custom-counter.js
class CustomCounter extends HTMLElement {
static get htmlTagName() { return "custom-counter"; }
constructor() {
super();
}
#shadow;
#render() {
console.log("#render called");
if(this.#shadow) {
this.#shadow.getElementById("counter").textContent = `* ${this.counter.toString()} *`;
}
}
get counter() {
const attName = "counter";
return this.hasAttribute(attName) ? parseInt(this.getAttribute(attName)) : 0;
}
set counter(value) {
const attName = "counter";
this.setAttribute(attName, value);
this.dispatchEvent(new CustomEvent("counterchanged", {detail: { value: this.counter}}));
}
connectedCallback() {
this.#shadow = this.attachShadow({mode:"closed"});
this.#shadow.innerHTML = `<div><span id="counter">* ${this.counter} *</span></div>`;
const element = this.#shadow.getElementById("counter");
element.style.background = "yellow";
element.addEventListener("click", event => ++this.counter);
// I added this code block:
if(this.hasAttribute("oncounterchanged")) {
const code = this.getAttribute("oncounterchanged");
if(code){
this.addEventListener("counterchanged", new Function(code));
}
}
}
attributeChangedCallback(attName, oldValue, newValue) {
console.log(`The '${attName}' attribute changed. oldValue: ${oldValue}. newValue: ${newValue}`);
this.#render();
}
static get observedAttributes() {
return ["counter"];
}
}
customElements.whenDefined(CustomCounter.htmlTagName).then(
() => console.log(`'${CustomCounter.htmlTagName}' web component is defined.`));
if(!customElements.get(CustomCounter.htmlTagName)) {
customElements.define(CustomCounter.htmlTagName, CustomCounter);
}
// It works fine:
/* const counter = document.getElementById("counter");
counter.addEventListener("counterchanged", event => console.log(event.detail));
*/
推荐阅读
- angular - Angular2 + ng2-smart-table:单击编辑按钮时获取单个单元格的值
- c++ - 如何在 C++ 中将一个对象从一个向量复制到另一个向量?
- java - 有没有办法将 Java TreeMap 序列化为 JSON?
- r - 使用R计算另一个文件中一个文件元素的频率
- c# - 如何从mvc中的动态表中检索数据
- video.js - 使用 video.js 进行低延迟 RTMP 播放
- python - 具有最小二乘的回溯线搜索算法实现
- amazon-redshift - Redshift - 根据过滤条件从表中提取数据
- java - 响应式 Jersey HTTP 客户端缺少依赖项
- python - 为什么 df['text'].str.contains('.') 总是返回 True?