Shadow DOM en JavaScript
Este artículo explica el Shadow DOM en JavaScript.
YouTube Video
javascript-html-shadow-dom.html
1<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>JavaScript & HTML</title>
6 <style>
7 * {
8 box-sizing: border-box;
9 }
10
11 body {
12 margin: 0;
13 padding: 1em;
14 padding-bottom: 10em;
15 font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
16 background-color: #f7f9fc;
17 color: #333;
18 line-height: 1.6;
19 }
20
21 .container {
22 max-width: 800px;
23 margin: 0 auto;
24 padding: 1em;
25 background-color: #ffffff;
26 border: 1px solid #ccc;
27 border-radius: 10px;
28 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
29 }
30
31 h1, h2 {
32 font-size: 1.2rem;
33 color: #007bff;
34 margin-top: 0.5em;
35 margin-bottom: 0.5em;
36 border-left: 5px solid #007bff;
37 padding-left: 0.6em;
38 background-color: #e9f2ff;
39 }
40
41 button {
42 display: block;
43 margin: 1em auto;
44 padding: 0.75em 1.5em;
45 font-size: 1rem;
46 background-color: #007bff;
47 color: white;
48 border: none;
49 border-radius: 6px;
50 cursor: pointer;
51 transition: background-color 0.3s ease;
52 }
53
54 button:hover {
55 background-color: #0056b3;
56 }
57
58 #output {
59 margin-top: 1em;
60 background-color: #1e1e1e;
61 color: #0f0;
62 padding: 1em;
63 border-radius: 8px;
64 min-height: 200px;
65 font-family: Consolas, monospace;
66 font-size: 0.95rem;
67 overflow-y: auto;
68 white-space: pre-wrap;
69 }
70
71 .highlight {
72 outline: 3px solid #ffc107; /* yellow border */
73 background-color: #fff8e1; /* soft yellow background */
74 transition: background-color 0.3s ease, outline 0.3s ease;
75 }
76
77 .active {
78 background-color: #28a745; /* green background */
79 color: #fff;
80 box-shadow: 0 0 10px rgba(40, 167, 69, 0.5);
81 transition: background-color 0.3s ease, box-shadow 0.3s ease;
82 }
83 </style>
84</head>
85<body>
86 <div class="container">
87 <h2>HTML Sample</h2>
88 <div id="content"></div>
89 <div id="shadow-host">Shadow Root Element</div>
90 <my-card></my-card>
91 </div>
92
93 <div class="container">
94 <h1>JavaScript Console</h1>
95 <button id="executeBtn">Execute</button>
96 <div id="output"></div>
97 </div>
98
99 <div>
100 <h2>Slot Sample</h2>
101 <my-element>
102 <h3 slot="header">Header Content</h1>
103 <p slot="content">Main Content</p>
104 </my-element>
105 </div>
106
107 <script>
108 // Override console.log to display messages in the #output element
109 (function () {
110 const originalLog = console.log;
111 console.log = function (...args) {
112 originalLog.apply(console, args);
113 const output = document.getElementById('output');
114 output.textContent += args.map(String).join(' ') + '\n';
115 };
116 })();
117
118 document.getElementById('executeBtn').addEventListener('click', () => {
119 // Prevent multiple loads
120 if (document.getElementById('externalScript')) return;
121
122 const script = document.createElement('script');
123 script.src = 'javascript-html-shadow-dom.js';
124 script.id = 'externalScript';
125 //script.onload = () => console.log('javascript-html-shadow-dom.js loaded and executed.');
126 //script.onerror = () => console.log('Failed to load javascript-html-shadow-dom.js.');
127 document.body.appendChild(script);
128 });
129 </script>
130</body>
131</html>
Entendiendo el Shadow DOM
El Shadow DOM
es una poderosa característica del estándar de Web Components que permite la encapsulación de estilos y estructuras DOM dentro de los componentes. Esta característica evita la interferencia de estilos y scripts entre los componentes y el documento principal.
Shadow DOM
en JavaScript
El Shadow DOM
proporciona una forma de crear un árbol DOM limitado asociado con un elemento DOM regular. Este árbol en la sombra está aislado del documento general, donde los estilos y scripts externos no lo influyen, ni sus estilos y scripts internos se filtran.
Por ejemplo, si creas un componente de botón personalizado utilizando Shadow DOM
, sus estilos no interferirán con otros elementos en la página. De manera similar, los elementos con el mismo nombre de clase no entrarán en conflicto.
El contenido HTML normal fuera del Shadow DOM
se denomina Light DOM
.
Beneficios del Shadow DOM
-
Encapsulación
- El
Shadow DOM
separa el estilo y la funcionalidad, evitando conflictos con estilos y scripts globales.
- El
-
Reusabilidad
- Los componentes construidos con el
Shadow DOM
pueden reutilizarse en diferentes proyectos sin preocuparse por conflictos de estilos.
- Los componentes construidos con el
-
Mantenibilidad
- La encapsulación hace que la lógica y el estilo del componente sean independientes, facilitando la depuración y el mantenimiento.
Creando el Shadow DOM
Para usar el Shadow DOM
, necesitas adjuntar una raíz de sombra a un elemento HTML. Aquí tienes un ejemplo sencillo:.
1// Select the host element
2const hostElement = document.querySelector('#shadow-host');
3
4// Attach a shadow root
5const shadowRoot = hostElement.attachShadow({ mode: 'open' });
Explicación
Este código incluye los siguientes elementos:.
-
Elemento anfitrión
- Un elemento DOM normal al cual se adjunta la raíz shadow (en este caso,
#shadow-host
).
- Un elemento DOM normal al cual se adjunta la raíz shadow (en este caso,
-
Raíz Shadow
- La raíz del árbol shadow creada usando
attachShadow
.
- La raíz del árbol shadow creada usando
-
Modo
- En modo
open
, el JavaScript externo puede acceder a la raíz shadow medianteelement.shadowRoot
. Por otro lado, el modoclosed
no permite el acceso.
- En modo
Estilos dentro del Shadow DOM
Shadow DOM
tiene su propio ámbito de estilos. Los estilos definidos dentro del árbol sombra solo se aplican a los elementos de ese árbol. Aquí hay un ejemplo:.
1// Add content to the shadow root
2shadowRoot.innerHTML = `
3 <style>
4 p {
5 color: green;
6 }
7 </style>
8 <p>Scoped style inside Shadow DOM.</p>
9`;
Incluso si hay estilos en conflicto en el documento principal, no afectan al párrafo dentro del árbol shadow.
1const content = document.getElementById('content');
2content.innerHTML = `
3 <style>
4 p {
5 color: red;
6 }
7 </style>
8 <p>This is in the main DOM.</p>
9`;
- El párrafo dentro del
Shadow DOM
permanece verde, mientras que el externo es rojo.
Eventos dentro del Shadow DOM
Los eventos dentro del Shadow DOM
son similares a los eventos normales del DOM pero pueden comportarse de manera diferente en términos de propagación debido a la encapsulación.
Aquí hay un ejemplo:.
1// Add an event listener inside Shadow DOM
2shadowRoot.innerHTML = `
3 <div id="button-container">
4 <button id="shadow-button">Click Me</button>
5 </div>
6`;
7
8shadowRoot.querySelector('#shadow-button').addEventListener('click', (event) => {
9 console.log('Button : Button clicked inside Shadow DOM');
10 console.log(event.target);
11});
12
13shadowRoot.querySelector('#button-container').addEventListener('click', (event) => {
14 console.log('Container : Button clicked inside Shadow DOM');
15 console.log(event.target);
16});
17
18hostElement.addEventListener('click', (event) => {
19 console.log('Event bubbled to the host element');
20 console.log(event.target);
21});
22
23document.addEventListener('click', (event) => {
24 console.log('Document listener');
25 console.log(event.target);
26});
- Cuando se hace clic en el botón, ambos listeners se activan, demostrando el comportamiento de la propagación de eventos.
- Cuando un evento que se origina dentro del
Shadow DOM
se propaga hacia elLight DOM
, eltarget
del evento se reasigna al elemento host en lugar de la fuente original.- En este ejemplo,
event.target
dentro delShadow DOM
es el elementobutton
real, pero fuera delShadow DOM
se reemplaza por el elemento hostdiv
.
- En este ejemplo,
Shadow DOM
y Elementos Personalizados
Shadow DOM
y los elementos personalizados son componentes clave de los Web Components
. Son tecnologías utilizadas para crear componentes de interfaz de usuario reutilizables y encapsulados.
1class MyCard extends HTMLElement {
2 constructor() {
3 super();
4 const shadow = this.attachShadow({ mode: 'open' });
5 shadow.innerHTML = `
6 <style>
7 p {
8 color: blue;
9 }
10 </style>
11 <p>I'm inside shadow DOM</p>
12 `;
13 }
14}
15
16customElements.define('my-card', MyCard);
El documento principal contiene HTML como este:.
1<my-card></my-card>
- Al utilizar el
Shadow DOM
dentro de elementos personalizados, puedes construir componentes reutilizables que resisten los conflictos de estilos. En este código, se crea una etiqueta llamadamy-card
que se asocia con la claseMyCard
. El elemento<p>
dentro demy-card
no se ve afectado por estilos externos y siempre se muestra en azul.
Slots: Distribuyendo contenido del Light DOM
Los slots permiten proyectar el contenido del Light DOM
dentro del Shadow DOM
. Aquí hay un ejemplo:.
1class MyElement extends HTMLElement {
2 constructor() {
3 super();
4 const shadow = this.attachShadow({ mode: 'open' });
5
6 shadow.innerHTML = `
7 <style>
8 .container {
9 border: 2px solid #ccc;
10 padding: 16px;
11 border-radius: 8px;
12 font-family: sans-serif;
13 }
14 .header {
15 font-size: 1.2em;
16 color: darkblue;
17 margin-bottom: 8px;
18 }
19 .content {
20 font-size: 1em;
21 color: #333;
22 }
23 </style>
24 <div class="container">
25 <div class="header">
26 <slot name="header"></slot>
27 </div>
28 <div class="content">
29 <slot name="content"></slot>
30 </div>
31 </div>
32 `;
33 }
34}
35
36customElements.define('my-element', MyElement);
El documento principal contiene HTML como este:.
1<my-element>
2 <h3 slot="header">Header Content</h1>
3 <p slot="content">Main Content</p>
4</my-element>
- El elemento
slot
dentro delShadow DOM
muestra el contenido delLight DOM
que tiene el atributoslot
correspondiente.
Conclusión
Shadow DOM
es una herramienta vital para construir componentes web robustos, reutilizables y mantenibles. Al encapsular estilos y funcionalidades, reduce el potencial de conflictos y simplifica la gestión de la base de código.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.