El Modelo de Objeto de Documento (DOM) es una representación orientada a objetos de la estructura, estilos y contenido de nuestro HTML. Expone el documento como nodos y objetos, en una estructura de árbol, que los lenguajes de programación como JavaScript pueden manipular. El Shadow DOM permite añadir árboles DOM ocultos al árbol DOM del documento. Estos árboles DOM ocultos están aislados del árbol DOM padre, limitando el alcance del CSS al componente web, permitiendo que se repitan las clases y los IDs encontrados en el padre sin interacción. Así es como se logra la encapsulación de estilos y la creación de componentes web.
Cuando se utiliza el encapsulado de ShadowDom en Angular, la aplicación utiliza la implementación nativa del navegador. Al usar Emulated it simula la funcionalidad nativa del navegador para superar el soporte actualmente limitado, pero se aplican principios y reglas de estilo similares. (Para más detalles acerca de los tipos de encapsulado en Angular ver Encapsulado CSS en Angular)
Veamos más de cerca cómo funciona.
Componentes personalizados usando JavaScript de vainilla
Digamos que queremos crear un componente de chip que podamos reutilizar en cualquier parte de nuestro código sin la ayuda de Angular:
Un número limitado de navegadores actualmente soportan shadow DOM, usaron Chrome v. 68 en la demo.
HTML
12345678910111213<html;³³³$0027head$0027;³³³$0027metacharset=$0027utf-8$0027³³³$0027title$0027$0027;Vainilla JS</title$0027$0027³³$0027; linktype="text/css"/head,/corporativo,/h1,/vainilla JS</h1,/sección,/sección,/secuencia,/scriptsrc="main". js"³³³"Scriptsrc="main"³³³"/cuerpo³³³"³³³"³³³"/html³³³"³³³"³³³". HTMLCSS
1234567891011body{font-family: sans-serif;font-size:16px;padding:1rem;}section{background: ghostwhite;border: solid 1px lightgray;padding:1rem;}CSS
Nuestra página debería verse algo así:
Ahora para crear el componente personalizado. Nuestro archivo JS está actualmente en blanco, así que empecemos. Lo primero que tenemos que hacer es definir una clase que extienda HTMLElement y defina nuestro elemento. Este es nuestro anfitrión de sombra que contendrá nuestra raíz de sombra y el árbol de sombra.
JS
1234567891011classCustomChipextendsHTMLElement{constructor(){super();// Define aquí el componente web}}// customElements.define(nombre, constructor, opciones);customElements.define($0027custom-chip$0027,CustomChip);Javascript
En nuestro HTML:
HTML (Parcial)
123<sección; Definiremos el componente de la red dentro del constructor. Lo primero que vamos a hacer es adjuntar una raíz de sombra a nuestro anfitrión de sombra.JS (Parcial)
123456789classCustomChipextendsHTMLElement{constructor(){super();// Crear una sombra rootconst shadow =this.attachShadow({ mode:$0027open$0027});}}Javascript
Utilizamos un modo de apertura porque nos permite acceder a la sombra DOM usando JavaScript. Si usamos cerrado, miCustomElem.shadowRoot volvería a ser nulo, y no podríamos acceder a él desde fuera de la sombra DOM.
Como podemos ver en las herramientas de desarrollo, ahora tenemos una instancia de nuestro recién creado componente con una sombra DOM.
Como estamos creando un chip, añadamos una etiqueta y un avatar. Mientras que todavía está dentro del constructor, creamos elementos HTML y luego los adjuntamos al DOM de la sombra. Estos elementos forman el árbol de sombra.
JS (Parcial)
&lt;pre&gt;12345678910111213141516171819202122232425// Crear envoltorio containerconst =documento.createElement(&apos;div&apos;);// Añadir avatarconst avatar =documento.createElement(&apos;img&apos;);let imgUrl;if(this. hasAttribute(&apos;img&apos;)){ imgUrl =this.getAttribute(&apos;img&apos;);}else{ imgUrl =&apos;assets/default.png&apos;;}avatar.src= imgUrl;// Añadir textconst info =documento. createElement(&apos;span&apos;);info.setAttribute(&apos;class&apos;,&apos;info&apos;);// Tomar el contenido del atributo y ponerlo dentro del texto info spanconst =esto. getAttribute(&apos;label&apos;);info.textContent= text;// adjuntar los elementos creados a la sombra dom shadow.appendChild(wrapper); wrapper.appendChild(avatar); wrapper.appendChild(info);&lt;/pre&gt;
JavascriptComo estamos sacando el texto de nuestro chip del atributo de la etiqueta, vamos a añadir uno a nuestro HTML. Lo mismo para la ruta de la imagen.
HTML (Parcial)
&lt;pre&gt;1234&amp;lt;seccion&amp;lt;&amp;lt;;costumbre-chiplabel=&quot;Foo&quot;img=&quot;activos/purpura. png&quot;&amp;gt;&amp;lt;/custom-chip&amp;gt;&amp;lt;custom-chiplabel=&quot;Bar&quot;&amp;gt;&amp;lt;/custom-chip&amp;gt;&lt;/sección&amp;gt;&lt;/pre&gt;
HTMLAhora tenemos un avatar y algo de texto dentro de un contenedor, así que vamos a darle estilo. Aún así, en el constructor, añadimos los estilos y luego los adjuntamos a la sombra DOM.
JS (Parcial)
&lt;pre&gt;12345678910111213141516171819202122// Style the compontconst style =documento.createElement(&apos;style&apos;);console.log(style.isConnected);style.textContent=` :host { background: lightgray; border-radius: 40px; pantalla: bloque en línea; familia de fuentes: sans-serif; acolchado: .5rem 1rem; } img { altura: 1rem; margen-derecha: .5rem; alineación vertical: línea de base; }`// adjuntar estilos a sombra DOMshadow.appendChild(style);console.log(style.isConnected);&lt;/pre&gt;
JavascriptNuestro componente recién creado tiene un alcance, un estilo y está listo para ser usado.