I Web Components sono una tecnologia web standard che ti permette di creare elementi HTML personalizzati e riutilizzabili. In questa guida vedremo come creare custom tag senza utilizzare il Shadow DOM, mantenendo la semplicità ma ottenendo comunque funzionalità avanzate.
Un Web Component è un elemento HTML personalizzato che puoi definire usando JavaScript. È composto principalmente da:
Ogni Web Component estende la classe HTMLElement e deve essere registrato con customElements.define():
class MioElemento extends HTMLElement {
constructor() {
super();
// Inizializzazione dell'elemento
}
connectedCallback() {
// Chiamato quando l'elemento viene aggiunto al DOM
this.render();
}
disconnectedCallback() {
// Chiamato quando l'elemento viene rimosso dal DOM
}
attributeChangedCallback(name, oldValue, newValue) {
// Chiamato quando un attributo osservato cambia
}
static get observedAttributes() {
// Lista degli attributi da osservare
return ['attributo1', 'attributo2'];
}
render() {
// Logica per renderizzare il contenuto
this.innerHTML = `<div>Contenuto del mio elemento</div>`;
}
}
// Registrazione del custom element
customElements.define('mio-elemento', MioElemento);
Creiamo un contatore che può essere incrementato cliccandoci sopra:
class MyCounter extends HTMLElement {
constructor() {
super();
this.count = parseInt(this.getAttribute('start') || '0');
}
connectedCallback() {
this.render();
this.addEventListener('click', this.increment.bind(this));
}
increment() {
this.count++;
this.render();
// Emette un evento personalizzato
this.dispatchEvent(new CustomEvent('counterChanged', {
detail: { value: this.count },
bubbles: true
}));
}
render() {
this.innerHTML = `
<div class="count">${this.count}</div>
<div class="label">Clicca per incrementare</div>
`;
}
}
customElements.define('my-counter', MyCounter);
Una barra di progresso che si aggiorna automaticamente e risponde ai cambiamenti degli attributi:
class MyProgress extends HTMLElement {
constructor() {
super();
this.value = parseInt(this.getAttribute('value') || '0');
this.max = parseInt(this.getAttribute('max') || '100');
}
static get observedAttributes() {
return ['value', 'max'];
}
connectedCallback() {
this.render();
this.startAutoIncrement();
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === 'value') {
this.value = parseInt(newValue || '0');
} else if (name === 'max') {
this.max = parseInt(newValue || '100');
}
this.render();
}
startAutoIncrement() {
setInterval(() => {
if (this.value < this.max) {
this.value += Math.random() * 3;
if (this.value > this.max) this.value = this.max;
this.setAttribute('value', Math.floor(this.value));
}
}, 200);
}
render() {
const percentage = Math.min((this.value / this.max) * 100, 100);
this.innerHTML = `
<div class="progress-bar" style="width: ${percentage}%"></div>
<div class="progress-text">${Math.floor(this.value)}/${this.max}</div>
`;
}
}
customElements.define('my-progress', MyProgress);
Un interruttore che cambia stato e comunica con altri elementi:
class MyToggle extends HTMLElement {
constructor() {
super();
this.active = this.hasAttribute('active');
this.label = this.getAttribute('label') || 'Toggle';
}
connectedCallback() {
this.render();
this.querySelector('.toggle-container').addEventListener('click', this.toggle.bind(this));
}
toggle() {
this.active = !this.active;
if (this.active) {
this.setAttribute('active', '');
this.classList.add('active');
} else {
this.removeAttribute('active');
this.classList.remove('active');
}
// Emette evento di cambio stato
this.dispatchEvent(new CustomEvent('toggleChanged', {
detail: {
active: this.active,
label: this.label
},
bubbles: true
}));
}
render() {
this.innerHTML = `
<div class="toggle-container">
<div class="toggle-switch">
<div class="toggle-knob"></div>
</div>
<span class="toggle-label">${this.label}</span>
</div>
`;
if (this.active) {
this.classList.add('active');
}
}
}
customElements.define('my-toggle', MyToggle);
I Web Components possono comunicare tra loro attraverso eventi personalizzati. Ecco come gestire la comunicazione:
// Ascoltare eventi da altri components
document.addEventListener('counterChanged', (event) => {
console.log('Counter cambiato:', event.detail.value);
});
// Inviare dati a un component specifico
const myCounter = document.querySelector('my-counter');
myCounter.setAttribute('start', '50');
// Comunicazione parent-child
class ParentComponent extends HTMLElement {
connectedCallback() {
this.innerHTML = `
<my-counter start="0"></my-counter>
<div id="display">Valore: 0</div>
`;
this.addEventListener('counterChanged', (e) => {
this.querySelector('#display').textContent = `Valore: ${e.detail.value}`;
});
}
}
bubbles: true si propagano attraverso il DOM, permettendo una comunicazione efficace tra components.
I Web Components hanno diversi metodi del ciclo di vita che puoi utilizzare:
my-element)disconnectedCallback()observedAttributes per attributi dinamiciScegliere di non usare Shadow DOM offre diversi vantaggi:
I Web Components senza Shadow DOM offrono un ottimo compromesso tra funzionalità e semplicità. Sono perfetti per creare elementi riutilizzabili che si integrano naturalmente con il resto della pagina, mantenendo la possibilità di gestire stati complessi e interazioni avanzate.
Con gli esempi mostrati hai visto come creare components interattivi che:
Ora puoi iniziare a creare i tuoi Web Components personalizzati! 🚀