Es muy probable que en el contexto de frontend, estemos acostumbrados a trabajar con el DOM y lo conozcamos bien, sin embargo, es menos común trabajar con el Shadow DOM. Si comenzamos a profundizar en el tema, veremos que muchas veces se mencionan conceptos como Light DOM, que probablemente sean aún más desconocidos.
Como hemos mencionado en un artículo anterior, el Shadow DOM es un DOM particular que se encuentra aislado del árbol DOM principal de la página. Esto nos permite cosas interesantes como aislar CSS o Javascript dentro de un componente, de modo que sólo afecta a él mismo.
► Aprender más sobre el Shadow DOM
¿Shadow DOM oculta el DOM?
Al crear un Shadow DOM entran en escena nuevas características y casuísticas, por lo que conviene aprender como funcionan y profundizar en sus posibilidades. La primera de ellas, que quizás ya habrás descubierto, es que al añadir un Shadow DOM a un elemento, si teníamos contenido en dicho elemento, este desaparecerá y no se mostrará visualmente en el navegador.
Imaginemos que tenemos el siguiente componente donde no tenemos Shadow DOM:
<app-element>
<div class="container">Contenido previo del elemento</div>
</app-element>
Desde que escribamos una línea donde hagamos un this.attachShadow({ mode: "open" });
, comprobaremos que el contenido del componente deja de aparecer en el navegador. Sin embargo, si miramos en el código fuente, el código HTML sigue existiendo.
Esto no es un error, es el funcionamiento correcto del Shadow DOM y más adelante entenderemos el por qué.
¿Qué es el Light DOM?
Como su propio nombre sugiere, el Light DOM es la contrapartida del Shadow DOM. Es el nombre con el que se suele hacer referencia a la parte oculta del DOM principal que hemos ocultado con el Shadow DOM:
Justo en el momento que ejecutamos un .attachShadow()
, creamos un Shadow DOM que pasará a «ocultar» o oscurecer (con su sombra) al DOM que existía en el componente, de modo que desaparece de la página mostrada al usuario. Este DOM que desaparece se suele denominar Light DOM.
Componente sin Shadow DOM
Imaginemos un ejemplo donde tenemos un componente que no tiene Shadow DOM. El resultado es casi el mismo que usar un <div>
como contenedor (por ejemplo). Sin embargo, ganamos en modularidad al utilizar el componente, ya que si tenemos que utilizar múltiples veces el componente, con sólo escribir la etiqueta se replicará el mismo contenido:
class AppElement extends HTMLElement {
connectedCallback() {
this.innerHTML = "<div>Hello, friend!</div>";
}
};
customElements.define("app-element", AppElement);
<app-element></app-element>
Ahora hagamos un pequeño cambio en nuestro HTML. Vamos a añadir contenido en el interior del componente, de modo que nuestro <app-element>
contiene un <div>
con texto:
<app-element>
<div class="container">Contenido previo del elemento</div>
</app-element>
En este caso, lo que ocurrirá es que el navegador hará lo siguiente:
- Renderiza la etiqueta
<app-element>
y muestra su contenido. - Inmediatamente después, carga el componente, y se modifica el contenido existente.
Hemos modificado el contenido inicial del componente por uno nuevo. En este caso, todo es bastante intuitivo, ya que carecemos de un Shadow DOM.
Componente con Shadow DOM
Vamos a complicarlo un poco. Hasta ahora los ejemplos han sido sin Shadow DOM en el componente. Vamos a crear un componente donde creamos un Shadow DOM con encapsulamiento abierto.
customElements.define("app-element", class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
connectedCallback() {
this.shadowRoot.innerHTML = "<div>Hello, friend!</div>";
}
});
<app-element>
<div class="container">Contenido previo del elemento</div>
</app-element>
En este caso, lo que ocurrirá es que el navegador hará lo siguiente:
- Renderiza la etiqueta
<app-element>
y muestra su contenido. - Creamos un Shadow DOM en el componente.
- Se añade el nuevo contenido HTML en el Shadow DOM.
- Justo al crear el Shadow DOM, se muestra y se oculta el antiguo DOM.
En otras palabras, cuando adjuntamos un Shadow DOM, se oculta el Light DOM. Sin embargo, un dato importante: Aunque el Light DOM se oculta, sigue existiendo en la página. No se ha modificado ni eliminado. Si inspeccionamos el HTML con el navegador, veremos algo así:
<app-element>
#shadow-root (open)
<div>Hello, friend!</div>
<div class="container">Contenido previo del elemento</div>
</app-element>
El contenido tanto del Shadow DOM como del Light DOM están presentes en el código, sin embargo, el Shadow DOM por el mero hecho de existir, ya oculta visualmente cualquier contenido del Light DOM. ¿Por qué sigue existiendo el Light DOM si se oculta y no se muestra? Lo explicaremos un poco más adelante.
Ten presente que en el último ejemplo estamos utilizando
this.shadowRoot.innerHTML
para modificar el Shadow DOM. Si utilizaramosthis.innerHTML
modificaríamos sólo el Light DOM.