Light DOM y Shadow DOM

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.

DOM en WebComponents: Light DOM vs Shadow DOM

Shadow DOM

Como hemos mencionado en un artículo anterior, el Shadow DOM es un fragmento de DOM 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 repercuta dentro de su componente.

Esto puede ser una característica deseada en algunas situaciones, pero a medida que trabajamos con componentes y necesitamos cosas más específicas, puede que queramos profundizar en estos conceptos en lo que se puede hacer con ellos.

Light DOM

Como su propio nombre sugiere, el Light DOM es la contrapartida del Shadow DOM. Es el nombre con el que se considera el árbol DOM que contiene el custom element de un WebComponent, antes de crear un Shadow DOM:

Light DOM vs Shadow DOM

Justo en el momento que ejecutamos un .attachShadow(), creamos un Shadow DOM que pasará a «ocultar» o oscurecer (con su sombra) al Light DOM del componente, de modo que desaparece de la página mostrada al usuario.

Vamos a ejemplificarlo con algunos fragmentos de código, y ver cada uno de los casos que podríamos tener en un componente. Para simplificarlo lo máximo posible, voy a escribir el código en un sólo fragmento de HTML y de forma compacta.

Componente sin Shadow DOM

Este es probablemente el caso más básico, donde tenemos un custom element que contiene un pequeño árbol DOM (un simple div). Este árbol DOM sería el Light DOM de este primer ejemplo.

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:

<app-element></app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    connectedCallback() {
      this.innerHTML = "<div>Hello, friend!</div>";
    }
  });
</script>

Ahora hagamos un pequeño cambio: Vamos a añadir contenido en el interior del custom element (LightDOM) en la primera línea de la etiqueta <app-element>:

<app-element>Contenido previo del elemento</app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    connectedCallback() {
      this.innerHTML = "<div>Hello, friend!</div>";
    }
  });
</script>

En este caso, el Light DOM será reemplazado por el contenido que estamos sobreescribiendo con .innerHTML dentro del componente. Hemos modificado el Light 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.

<app-element>
  <div class="container">Contenido previo del elemento</div>
</app-element>

<script>
  customElements.define("app-element", class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({ mode: "open" });
    }

    connectedCallback() {
      this.shadowRoot.innerHTML = "<div>Hello, friend!</div>";
    }
  });
</script>

Justo en el momento de crear el Shadow DOM, el creado pasa a formar parte del componente y visualmente se oculta el contenido previo que teníamos en el custom element. En otras palabras, cuando adjuntamos un Shadow DOM, este oculta el Light DOM.

Sin embargo, un dato importante: Aunque el Light DOM se oculta, sigue existiendo en la página. Si inspeccionamos el código con el navegador, veremos algo parecido a esto:

<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.

Ten presente que en el último ejemplo estamos utilizando this.shadowRoot.innerHTML para modificar el Shadow DOM. Si utilizaramos this.innerHTML modificaríamos sólo el Light DOM.

Manz
Publicado por Manz

Docente, divulgador informático y freelance. Autor de Emezeta.com, es profesor en la Universidad de La Laguna y dirige el curso de Programación web FullStack y Diseño web FrontEnd de EOI en Tenerife (Canarias). En sus ratos libres, busca GIF de gatos en Internet.