玩弄自定义元素,我试图得到一个点击事件触发取决于自定义元素属性的值。但是使用attributeChangedCallback()
方法(以及connectedCallback()
方法),我最终得到了多个事件侦听器。
class HelloWorld extends HTMLElement {
static get observedAttributes() {
return ['attribute1', 'attribute2'];
}
get attribute1() {
return this.getAttribute('attribute1');
}
set attribute1(val) {
if (val) {
this.setAttribute('attribute1', val);
} else {
this.removeAttribute('attribute1');
}
}
get attribute2() {
return this.getAttribute('attribute2');
}
set attribute2(val) {
if (val) {
this.setAttribute('attribute2', val);
} else {
this.removeAttribute('attribute2');
}
}
constructor() {
super();
}
connectedCallback() {
this.textContent = 'Hello World';
update(this);
}
attributeChangedCallback(name, oldValue, newValue) {
update(this);
}
}
customElements.define('hello-world', HelloWorld);
function update(el) {
if (el.attribute1 === 'foo') {
el.addEventListener('click', e => {
el.textContent = (el.textContent.indexOf(' (') != -1 ? el.textContent.substring(0, el.textContent.indexOf(' (')) : el.textContent) + ' (clicked ' + (el.textContent.match(/d+/) ? parseInt(el.textContent.match(/-?d+/)[0]) + 1 : 1) + ' times)';
});
} else if (el.attribute1 === 'bar') {
el.addEventListener('click', e => {
el.textContent = (el.textContent.indexOf(' (') != -1 ? el.textContent.substring(0, el.textContent.indexOf(' (')) : el.textContent) + ' (clicked ' + (el.textContent.match(/d+/) ? parseInt(el.textContent.match(/-?d+/)[0]) - 1 : 1) + ' times)';
});
}
}
hello-world {
cursor: pointer;
}
<hello-world attribute1="foo" attribute2=""></hello-world>
为什么attributeChangedCallback()
方法总是调用两次,因此增加了两个事件侦听器?如何避免这种情况?最佳实践是什么?
它被称为attributeChangedCallback
,因此在每个单个上触发属性更改,包括元素添加到DOM时的init
在connectedCallback
中附加侦听器,但是可以如果在DOM中移动元素,则再次运行,因此必须在disconnectedCallback
中删除它们。更简单的方法可能是使用inlineEventHandler,元素上只能有一个。
customElements.define('hello-world', class extends HTMLElement {
static get observedAttributes() {
return ['attribute1', 'attribute2'];
}
get attribute1() {
return this.getAttribute('attribute1');
}
set attribute1(val) {
this.toggleAttribute('attribute1', val);
}
get attribute2() {
return this.getAttribute('attribute2');
}
set attribute2(val) {
this.toggleAttribute('attribute2', val);
}
connectedCallback() {
this.count = 0;
this.innerHTML = `Hello World clicked: <span>${this.count}</span> times`;
this.onclick = (evt) => {
this.count++;
this.querySelector("span").innerHTML = this.count;
}
}
attributeChangedCallback(name, oldValue, newValue) {
console.log("attributeChangedCallback", name, oldValue, newValue);
}
});
hello-world {
cursor: pointer;
}
<hello-world attribute1="foo" attribute2=""></hello-world>
constructor() {
super()
}
如果不需要,则不存在的constructor
将执行其父constructor
。这就是super()
所做的
如果你想防止多个监听器,你可以尝试一个方法:
addListeners(){
.. add your listeners
this.addListeners = () => {}; // overload; Don't run its original code again
}
还要注意,添加到元素(或其内容)上的侦听器会自动调用垃圾收集/在元素从DOM中移除时被移除。
添加到其他DOM元素上的任何侦听器(例如:你必须在disconnectedCallback