Lo Shadow DOM serve allâincapsulamento e permette al componente di avere il proprio DOM âshadowâ, al quale il documento principale non può accedere nemmeno accidentalmente. Inoltre può avere regole di stile con scope locale e molto altro ancora.
Shadow DOM built-in
Avete mai pensato a come, dei controlli così complessi come quelli del browser, vengono creati e stilizzati?
Prendiamo <input type="range"> come esempio:
Il browser usa la combinazione DOM e CSS internamente, per visualizzarli a schermo. Normalmente la struttura del DOM ci è invisibile, ma possiamo visualizzarla negli strumenti dello sviluppatore dei browser. Ad esempio, negli strumenti di sviluppo di Chrome, si può attivarne la visualizzazione nelle impostazioni generiche, lâopzione âShow user agent shadow DOMâ.
Così facendo <input type="range"> verrà mostrato in questo modo:
Quello che vediamo alla voce #shadow-root viene chiamato âShadow DOMâ.
Non possiamo lavorare sugli elementi built-in dello Shadow DOM tramite normale chiamate o selettori CSS. Non sono nodi figli normali, ma una potente tecnica di incapsulamento.
Nellâesempio precedente, possiamo notare lâattributo pseudo che è molto utile. Non è un attributo standard, esiste per ragioni storiche e possiamo usarlo per stilizzare i sottoelementi tramite CSS, in questo modo:
<style>
/* colora la traccia di rosso */
input::-webkit-slider-runnable-track {
background: red;
}
</style>
<input type="range">
Ripetiamolo ancora una volta, pseudo non è un attributo standard. Storicamente, i browser hanno cominciato a sperimentare con le strutture interne del DOM per implementare dei controlli, e con il passare del tempo, lo Shadow DOM è stato standardizzato per permettere a noi sviluppatori, di fare alla stessa maniera.
Più avanti, utilizzeremo lo standard Shadow DOM moderno, nella sezione delle specifiche DOM ed altre specifiche correlate.
Shadow tree
Un elemento DOM può contenere due tipi di sottoalberi:
- Light tree â un normale sottoalbero DOM, fatto di figli HTML. Tutti i sottoalberi affrontati nei capitoli precedenti appartengono a questa categoria âlightâ.
- Shadow tree â un sottoalbero DOM nascosto, senza un elemento corrispondente nellâHTML, nascosto da âocchi indiscretiâ.
Se un elemento li ha entrambi, il browser renderizza solo lo Shadow tree. Tuttavia possiamo impostare una sorta di composizione tra gli il light e lo Shadow tree. Vedremo in dettaglio lâargomento nellâapposita sezione Shadow DOM slots, composition.
Lo Shadow tree può essere usato allâinterno dei Custom Elements per nascondere i componenti interni ed applicare gli stili localmente allâinterno componente.
Per esempio, questo elemento, <show-hello> nasconde il suo DOM interno dellâ Shadow tree:
<script>
customElements.define('show-hello', class extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({mode: 'open'});
shadow.innerHTML = `<p>
Hello, ${this.getAttribute('name')}
</p>`;
}
});
</script>
<show-hello name="John"></show-hello>
Ecco come risulta il DOM, negli strumenti di sviluppo di Chrome, con il contenuto inserito in â#shadow-rootâ:
Inizialmente, la chiamata a elem.attachShadow({mode: â¦}) crea uno Shadow tree.
Ci sono due limitazioni:
- Possiamo creare solo una Shadow root per ogni elemento.
- Lâelemento
elemdeve essere, o un elemento personalizzato, o uno tra questi elementi: âarticleâ, âasideâ, âblockquoteâ, âbodyâ, âdivâ, âfooterâ, âh1â¦h6â, âheaderâ, âmainâ ânavâ, âpâ, âsectionâ, o âspanâ. Altri elementi, come ad esempio,<img>, non possono contenere uno Shadow tree.
Lâopzione mode imposta il livello di incapsulamento. Le opzioni possibili sono:
-
"open"â la Shadow root è disponibile tramiteelem.shadowRoot.Lo Shadow tree di
elemè accessibile da qualunque punto del codice. -
"closed"âelem.shadowRootè semprenull.Lo Shadow DOM è accessibile esclusivamente dal riferimento restituito da
attachShadow(il quale, con ogni probabilità , sarà nascosto dentro una classe. Shadow tree nativi del browser, come<input type="range">, appartengono a questa categoria, e non câè modo di accedervi.
La Shadow root, restituita con attachShadow, è come un elemento: possiamo usare innerHTML o i metodi DOM, come append, per popolarlo di elementi.
Lâelemento con una Shadow root viene invece chiamato âShadow tree hostâ, ed è disponibile attraverso la proprietà host della Shadow root:
// supponiamo di avere {mode: "open"}, altrimenti elem.shadowRoot sarebbe null
alert(elem.shadowRoot.host === elem); // true
Incapsulamento
Vi è una separazione marcata tra lo Shadow DOM ed il documento principale:
- Gli elementi dello Shadow DOM non sono rilevabili tramite
querySelectordel light DOM. In particolare, gli id degli elementi dello Shadow DOM, potrebbero andare in conflitto con quelli dellâalbero del light DOM. - Lo Shadow DOM ha i suoi fogli di stile e le regole di stile del DOM esterno non vengono applicate.
Per esempio:
<style>
/* lo stile del documento non viene applicato allo Shadow tree contenuto in #elem (1) */
p { color: red; }
</style>
<div id="elem"></div>
<script>
elem.attachShadow({mode: 'open'});
// Lo Shadow tree possiede un proprio stile (2)
elem.shadowRoot.innerHTML = `
<style> p { font-weight: bold; } </style>
<p>Hello, John!</p>
`;
// <p> e' visibile solamente da queries dentro lo Shadow tree (3)
alert(document.querySelectorAll('p').length); // 0
alert(elem.shadowRoot.querySelectorAll('p').length); // 1
</script>
- Lo stile del documento non influenza lo Shadow tree.
- â¦ma lo stile allâinterno sì.
- Per avere gli elementi dentro lo Shadow tree, dobbiamo fare le query da dentro lâalbero.
Riferimenti
- DOM: https://dom.spec.whatwg.org/#shadow-trees
- Compatibilità : https://caniuse.com/#feat=shadowdomv1
- Lo Shadow DOM viene menzionato in molte altre specifiche, ad esempio in DOM Parsing specifica che la Shadow root possiede la proprietÃ
innerHTML.
Riepilogo
Lo âShadow DOMâ è una modalità di creazione di un componente DOM locale.
- Il comando
shadowRoot = elem.attachShadow({mode: open|closed})crea uno Shadow DOM perelem. Semode="open", allora sarà possibile accedervi attraverso la proprietÃelem.shadowRoot. - Possiamo popolare
shadowRootusandoinnerHTMLo altri metodi DOM.
Gli elementi dello Shadow DOM:
- Hanno la loro area per gli id
- Sono invisibili ai selettori JavaScript dal documento principale, se cercati con
querySelector - Usano gli stili dello Shadow tree, e non quelli del documento principale.
Lo Shadow DOM, se esiste, viene renderizzato dal browser al posto del cosiddetto âlight DOMâ (normali nodi figli). Nel capitolo Shadow DOM slots, composition vedremo come comporli.
Commenti
<code>, per molte righe â includile nel tag<pre>, per più di 10 righe â utilizza una sandbox (plnkr, jsbin, codepenâ¦)