- Learn
- Guida ai Web Components
- Usare lo shadow DOM
Usare lo shadow DOM
Il web component che abbiamo realizzato fino a qui risponde alle nostre esigenze:
- visualizza tramite un numero di stelle colorate il valore assegnato staticamente nel markup
- visualizza il medesimo valore tramite assegnamento via JavaScript
- modifica il valore corrente e la relativa visualizzazione in seguito all’interazione con l’utente
Abbiamo quindi un componente a tutti gli effetti riusabile in pagine diverse e in progetti diversi.
Tuttavia, la sua struttura interna non è del tutto indifferente al contesto esterno. In altre parole, il comportamento del nostro componente segue le nostre aspettative all’interno di una pagina HTML semplice come quella usata nel nostro esempio, ma chi ci garantisce che non ci siano condizioni tali per cui il contesto della pagina non possa influire sull’aspetto o sul comportamento del nostro componente?
Proviamo a chiarire questo concetto con un esempio. Supponiamo di inserire il nostro componente all’interno di una pagina HTML che ha già una definizione di stile come la seguente:
<style>
span {
background-color: red;
}
</style>
Questa regola CSS assegna il rosso come colore di sfondo a ciascun elemento <span>
presente nella pagina. Se inseriamo il nostro componente in questa pagina, otterremo un effetto grafico analogo a quello mostrato dalla seguente figura:

Dal momento che il nostro componente utilizza al suo interno l’elemento <span>
, anch’esso verrà influenzato dalla definizione della regola CSS. Probabilmente non è l’effetto che vorremmo. Quello che ci aspetteremmo è che l’implementazione interna del nostro componente fosse immune da influenze esterne.
Naturalmente, il problema non si limita soltanto all’applicazione degli stili CSS. La struttura interna del nostro componente potrebbe essere intaccata anche da codice JavaScript come quello mostrato di seguito:
window.onload = function() {
let ratings = document.querySelectorAll("span");
ratings.forEach(element => {
element.innerHTML = "Hacked!"
});
Questo script sostituisce il markup interno di tutti gli elementi <span>
contenuti nella pagina con il testo Hacked!, generando il seguente effetto sul nostro componente:

Naturalmente quelli mostrati sono casi limite, ma ci servono per mostrare come la struttura interna del componente così come l’abbiamo realizzato non è indifferente all’ambiente in cui viene inserito.
Come possiamo isolare la struttura interna del nostro web component in modo da avere un maggior controllo sul suo aspetto e di consentire eventuali personalizzazioni soltanto tramite attributi e proprietà? L’utilizzo dello shadow DOM è la risposta a questa domanda.
Lo shadow DOM è un insieme di API standard che supportano l’incapsulamento di stile e markup per un web component in modo tale da non subire gli effetti che abbiamo visto prima. Grazie all’uso dello shadow DOM, possiamo associare ad un componente un DOM privato, proprio come se fosse il DOM di una pagina HTML, il cui markup e stile non è influenzato dalla pagina che ospita il componente.
Iniziamo a vedere come utilizzare lo shadow DOM applicandolo al nostro componente per evitare gli effetti evidenziati prima. A questo scopo, modifichiamo il costruttore del nostro componente come mostrato di seguito:
constructor() {
super();
this._maxValue = 5;
this._value = 0;
this.attachShadow({mode: "open"});
}
Rispetto al codice precedente, notiamo l’invocazione del metodo attachShadow()
dell’elemento corrente, cioè del web component stesso. Questo metodo è disponibile per qualsiasi elemento HTML ed è il metodo che fornisce il supporto per lo shadow DOM. Essenzialmente esso genera la root per un DOM che andremo a costruire nel metodo createComponent()
e che sarà protetto dall’ambiente esterno. Per inciso, spesso lo shadow DOM è anche detto shadow tree.
Notiamo che il metodo attachShadow()
richiede che venga passato un oggetto come parametro per specificare la modalità di creazione del DOM. Nel nostro esempio abbiamo impostato il valore “open” come modalità del DOM. Ciò vuol dire che il nostro DOM risulterà accessibile dall’esterno via JavaScript. Il valore alternativo è “closed”, che impedisce completamente l’accesso alla struttura dello shadow DOM dall’esterno.
Dopo aver fatto questa modifica al costruttore, adeguiamo il metodo createComponent()
come mostrato dal seguente codice:
createComponent() {
let style = document.createElement("style");
style.appendChild(document.createTextNode(styleRules));
this.shadowRoot.appendChild(style);
let starList = this.createStarList();
this.shadowRoot.appendChild(starList);
}
La differenza che notiamo rispetto alla versione precedente del codice è l’accesso alla proprietà shadowRoot
per l’aggancio degli elementi del DOM creati, invece dell’aggancio diretto a this
. Questa proprietà espone lo shadow DOM del componente creato nel costruttore dal metodo attachShadow()
, in modo tale che sia accessibile via JavaScript.
Naturalmente occorrerà adeguare anche il resto del codice, facendo in modo che utilizzi la shadowRoot
per accedere agli elementi interni del componente invece che direttamente all’oggetto this
. È questo il caso del metodo replaceStarList()
, che andremo a modificare come segue:
replaceStarList() {
let starNode = this.shadowRoot.children[1];
if (starNode) {
let starList = this.createStarList();
starNode.remove();
this.shadowRoot.appendChild(starList);
}
}
A questo punto il nostro web component risulterà protetto da regole CSS o da codice JavaScript esterni che operano su elementi generici.
Se vuoi aggiornamenti su JavaScript inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
I Video di HTML.it
Semplificare la gestione di un progetto con Angular CLI
Con Angular 2 abbiamo diverse novità e anche qualche complessità da affrontare nella costruzione di un’applicazione: dal transpiling al bundling, […]