Jugando con elementos personalizados, estoy tratando de activar un evento de clic según el valor del atributo del elemento personalizado. Pero usando el attributeChangedCallback() (junto con el método connectedCallback() ) estoy terminando con múltiples detectores de eventos.
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> ¿Por qué el método attributeChangedCallback() siempre se llama dos veces y, por lo tanto, agrega dos detectores de eventos? ¿Cómo evitar esto? ¿Cuál sería la mejor práctica?
Se llama attributeChangedCallback , por lo que se activa en cada cambio de atributo, incluido init cuando el elemento se agrega al DOM.
Adjunte oyentes en la devolución de llamada connectedCallback , pero eso puede ejecutarse nuevamente si mueve el elemento en el DOM, por lo que debe eliminarlos en la devolución de disconnectedCallback .
Más fácil podría ser usar un EventHandler en línea , solo puede haber uno en un elemento.
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() } no es necesario, un constructor no existente ejecutará el constructor desde su padre... que es lo que hace super() .
Si desea evitar múltiples oyentes, puede probar un método:
addListeners(){ .. add your listeners this.addListeners = () => {}; // overload; Don't run its original code again } También tenga en cuenta que los oyentes que agrega en el elemento (o su contenido) se recolectan / eliminan automáticamente cuando el elemento se elimina del DOM.
Cualquier oyente que agregue en otros elementos DOM (por ejemplo, documento) debe eliminarse en la devolución de disconnectedCallback