Los componentes web son más fáciles de lo que piensas

Cuando fui a conferencias y vi presentaciones sobre el tema de los componentes web, siempre pensé que no solo era elegante, sino también bastante difícil. Mil líneas de JavaScript para almacenar solo 4 líneas de HTML. El orador escondió inevitablemente una gran cantidad de código JS detrás de cosas simples o se sumergió en detalles complejos, luego mis ojos comenzaron a cerrarse por el aburrimiento y comencé a pensar si mi asignación diaria cubría el costo de los bocadillos.





Sin embargo, en un proyecto reciente hecho para aprender HTML fácilmente (por supuesto, agregando zombies y bromas tontas), decidí que era necesario describir cada elemento HTML en la especificación. Aparte de esa conferencia, primero comencé con la introducción <slot>



y <template>



los elementos, y cuando quise escribir algo interesante sobre ellos en el proyecto, tuve que ahondar en el tema.





Y a medida que profundicé, me di cuenta de que los componentes web son más fáciles de lo que pensaba.





O los componentes web han recorrido un largo camino desde que soñé con comer bocadillos en una conferencia, o dejé que mi miedo inicial se interpusiera en el camino para conocerlos realmente, o tal vez ambos.





Estoy aquí para decirte que sí, puedes crear un componente web. Pongamos miedo e incluso bocadillos fuera de la puerta para juntarlo todo.





Comencemos con una <template>

<template>



es un elemento HTML que nos permite crear una plantilla (estructura HTML para componentes web).





El código
<template>
  <p>The Zombies are coming!</p>
</template>
      
      



El elemento <template>



es muy importante porque te permite mantener todo junto. Es como una base para tu hogar, una base a partir de la cual todo lo que llamamos edificio terminado comienza a construirse. Usemos este pequeño fragmento de código para nuestro <apocalyptic-warning>



componente que nos notifica del próximo apocalipsis zombie.





Luego está el componente <slot>

<slot>



es solo un elemento HTML, como <template>



. Pero en nuestro caso, <slot>



configura lo que <template>



muestra en la página.





El código
<template>
  <p>The <slot>Zombies</slot> are coming!</p>
</template>
      
      



"Zombies" ( ?) <template>



. , . "Zombies".





<slot>



placeholder



. placeholder



, , - placeholder



. - name



.





<template>
  <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



name



- , <template>



. "whats-coming" . , -, <slot>



, - , , .





-

, , ( : JS , ).





<apocalyptic-warning>
  <span slot="whats-coming">Halitosis Laden Undead Minions</span>
</apocalyptic-warning>

<template>
  <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



, ? <apocalyptic-warning>



, HTML . <span>



"whats-coming". <span>



"Zombies", .





, - , HTML , . , , -.





? , ? , . , <slot>



, JavaScript.





, JavaScript , , , , . , , .





-, -. : , .





, .





//   -   

customElements.define("apocalyptic-warning", class extends HTMLElement {
  	  //  ,          HTML 
  
  	//   ,    
    constructor() {

      //   , .   HTMLElement.      HTML .
      super();

      //  <template>      `warinng`
      let warning = document.getElementById("warningtemplate");
      
      //      `mywarning`
      let mywarning = warning.content;

      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));
    }
});
      
      



, .





const shadowRoot = this.attachShadow({mode: "open"}).appendChild(mywarning.cloneNode(true));
      
      



. -, - (this) , Shadow DOM.{ mode: open }



, JavaScript :root



Shadow DOM , - . Shadow DOM ( : HTML Node). , . , Shadow DOM , <slot>



slot , .





. -, , .





JS:





customElements.define('apocalyptic-warning',
    class extends HTMLElement {
      constructor() {
        super();
        let warning = document.getElementById('warningtemplate');
        let mywarning = warning.content;
        
         const shadowRoot = this.attachShadow({mode: 'open'}).appendChild(mywarning.cloneNode(true));
        
      }
    });
      
      



HTML:





<p>The Apocalypse will never happen!</p>
<apocalyptic-warning>
   <span slot="whats-coming">Undead</span>
</apocalyptic-warning>
<apocalyptic-warning>
   <span slot="whats-coming">Halitosis Laden Zombie Minions</span>
</apocalyptic-warning>
<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>
    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



: Codepen





, . , CSS. , <style>



<template>



.





<template id="warningtemplate">
  <style>
    p {
      background-color: pink;
      padding: 0.5em;
      border: 1px solid red;
    }
  </style>

    <p>The <slot name="whats-coming">Zombies</slot> are coming!</p>
</template>
      
      



, , Shadow DOM.





, , , , , Shadow DOM. , Shadow DOM DOM , . - , DOM .





, , <style>,



<slot>



. , . - , CSS , , . , -, CSS.





apocalyptic-warning span {
  color: blue;
}
      
      



, CSS <template>



.





JavaScript , , , , <zombie-profile>.







customElements.define("zombie-profile",
  class extends HTMLElement {
    constructor() {
      super();
      let profile = document.getElementById("zprofiletemplate");
      let myprofile = profile.content;
      const shadowRoot = this.attachShadow({mode: "open"}).appendChild(myprofile.cloneNode(true));
    }
  }
);
      
      



HTML CSS.





<template id="zprofiletemplate">
  <style>
    img {
      width: 100%;
      max-width: 300px;
      height: auto;
      margin: 0 1em 0 0;
    }
    h2 {
      font-size: 3em;
      margin: 0 0 0.25em 0;
      line-height: 0.8;
    }
    h3 {
      margin: 0.5em 0 0 0;
      font-weight: normal;
    }
    .age, .infection-date {
      display: block;
    }
    span {
      line-height: 1.4;
    }
    .label {
      color: #555;
    }
    li, ul {
      display: inline;
      padding: 0;
    }
    li::after {
      content: ', ';
    }
    li:last-child::after {
      content: '';
    }
    li:last-child::before {
      content: ' and ';
    }
  </style>

  <div class="profilepic">
    <slot name="profile-image"><img src="https://assets.codepen.io/1804713/default.png" alt=""></slot>
  </div>

  <div class="info">
    <h2><slot name="zombie-name" part="zname">Zombie Bob</slot></h2>

    <span class="age"><span class="label">Age:</span> <slot name="z-age">37</slot></span>
    <span class="infection-date"><span class="label">Infection Date:</span> <slot name="idate">September 12, 2025</slot></span>

    <div class="interests">
      <span class="label">Interests: </span>
      <slot name="z-interests">
        <ul>
          <li>Long Walks on Beach</li>
          <li>brains</li>
          <li>defeating humanity</li>
        </ul>
      </slot>
    </div>

    <span class="z-statement"><span class="label">Apocalyptic Statement: </span> <slot name="statement">Moooooooan!</slot></span>

  </div>
</template>
      
      



CSS <zombie-profile>



CSS. , , , <template>



.





zombie-profile {
  width: calc(50% - 1em);
  border: 1px solid red;
  padding: 1em;
  margin-bottom: 2em;
  display: grid;
  grid-template-columns: 2fr 4fr;
  column-gap: 20px;
}
zombie-profile img {
  width: 100%;
  max-width: 300px;
  height: auto;
  margin: 0 1em 0 0;
}
zombie-profile li, zombie-profile ul {
  display: inline;
  padding: 0;
}
zombie-profile li::after {
  content: ', ';
}
zombie-profile li:last-child::after {
  content: '';
}
zombie-profile li:last-child::before {
  content: ' and ';
}
      
      



:





: Codepen





, , , -, . , , - , , .





Eso es todo. ¿De qué tienes más miedo ahora: los componentes web o un apocalipsis zombi? En un pasado no muy lejano, podría haber dicho que los componentes web, pero ahora los zombis son lo único que me preocupa (bueno, y mi asignación diaria cubrirá los costos de los bocadillos).








All Articles