Slots en Shadow DOM

Trabajando con ranuras del DOM


En el artículo anterior vimos como crear un Shadow DOM para crear DOM particulares o locales en elementos HTML específicos. Si recordamos, vimos que el DOM que teníamos en el elemento (Light DOM), era oscurecido por el Shadow DOM, y en el navegador pasaban a ser renderizados sólo los elementos del Shadow DOM.

¿Qué son los slots?

Los slots o ranuras son una característica del DOM mediante la cuál podemos insertar fragmentos del Light DOM en nuestro Shadow DOM. Se puede ver como «pequeños huecos» en el Shadow DOM por donde se pueden ver partes de luz que realmente están en el Light DOM.

Veamos un ejemplo sencillo:

const container = document.querySelector('.container');

container.attachShadow({ mode: "open" });
container.shadowRoot.innerHTML = `<div><h1>Shadow DOM</h1><slot></slot></div>`;
<div class="container">
  <p>El contenido original del DOM.</p>
</div>

Como podemos ver, en este caso, el elemento <slot> deja pasar todo el contenido original de la etiqueta .container al Shadow DOM.

Slots múltiples

Para casos básicos, nos basta con utilizar un sólo <slot> que obtiene el contenido íntegro del Light DOM. Sin embargo, podemos utilizar múltiples <slot> para insertar fragmentos específicos:

const container = document.querySelector('.container');

container.attachShadow({ mode: "open" });
container.shadowRoot.innerHTML = `<div>
  <slot name="title">Título sin definir</slot>
  <slot name="description">Descripción sin definir</slot>
</div>`;

// Tras 4 seg, eliminamos el contenido del LightDOM
setTimeout(() => container.innerHTML = '', 4000);
<div class="container">
  <h1 slot="title">Este es el título</h1>
  <p slot="description">Esta es la descripción</p>
</div>

Observa que en este caso, hemos utilizado las etiquetas <slot> añadiéndole un atributo name para darles un nombre. Luego, en el Light DOM, hemos definido la etiqueta que nos interesa, añadiéndole un atributo slot que coincida con el name del slot del Shadow DOM.

Además, fíjate en el setTimeout() que hemos definido para eliminar el contenido del Light DOM a los 4 segundos. De esta forma se ve claramente, que si no hay una etiqueta con atributo slot, el Shadow DOM utiliza el contenido del slot como contenido a utilizar por defecto.

Si quieres aprender más sobre los slots, puedes consultar el artículo Slots en WebComponents.

Acceso a los slots desde Javascript

Si queremos hacer alguna acción concreta, mediante Javascript podemos acceder a los slots o a los elementos insertados en los slots, a través de varias propiedades o métodos:

PropiedadDescripción
Slot del Shadow DOM
.nameDevuelve el nombre del slot (simplemente, su atributo HTML).
.assignedElements()Devuelve un array con los elementos asignados al slot.
.assignedNodes()Devuelve un array con los nodos (elementos y textos) asignados al slot.
Elemento insertado en el slot
.slotDevuelve el nombre del slot referenciado (simplemente, su atributo HTML).
.assignedSlotDevuelve el slot donde se ha asignado este elemento.

Como puedes ver, los <slot> pueden tener varios elementos asignados, simplemente referenciandolo por el nombre. La diferencia entre .assignedElements() y .assignedNodes() es que el primero se basa en y el segundo en (que incluye elementos y nodos de texto).

Dando estilo a los slots

El contenido que está dentro de un <slot> no se puede estilar desde el Shadow DOM, ya que pertenece al DOM global. Sólo podemos estilarlo desde el DOM global, o desde el Shadow DOM utilizando el pseudoelemento ::slotted() para dar estilo a los elementos insertados en el slot:

const container = document.querySelector('.container');

container.attachShadow({ mode: "open" });
container.shadowRoot.innerHTML = `<div>
  <style>
    h1 { color: white; background: indigo; }
    ::slotted(p) { background: steelblue; }
  </style>
  <h1>Shadow DOM</h1>
  <slot></slot>
</div>`;
<style>
  h1 { color: red }
  p { color: white; padding: 10px; }
</style>
<div class="container">
  <p>El contenido original del DOM.</p>
</div>

Observa que en el caso del <h1> el CSS global no afecta al <h1> del Shadow DOM (está encapsulado). Sin embargo, las ranuras slot si que afectan ambos. El CSS global afecta a la etiqueta <p> porque está en el DOM global, pero también podemos afectarla mediante ::slotted() en el Shadow DOM.

Mediante ::slotted() sólo se pueden estilar los elementos de primer nivel. Si quisieramos darle estilo a los elementos hijos anidados, no podríamos hacerlo. En esos casos, mi recomendación es investigar como funciona CSS Parts y su pseudoelemento ::part().

Los slots en el SEO

Los <slot> son un mecanismo muy interesante en el SEO (Optimización en motores de búsqueda), sobre todo cuando utilizamos Javascript para crear el DOM.

Utilizando <slot>, podemos añadir todo el contenido sensible a ser indexado en el Light DOM, mientras que en el Shadow DOM con Javascript, añadimos sólo la parte que no sea interesante de cara a buscadores.

Tradicionalmente, en el contexto de SEO, Google y otros motores de búsqueda pueden no indexar completamente tu sitio web si está construido con Javascript de cliente. Más información en este excelente post de Vercel: How Google handles Javascript throughout the indexing process.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev