From 1109132f09d75da9a28b649c7677bb6ce07c40c0 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:41:45 -0500 Subject: initial commit --- .../web/web_components/custom_elements/index.html | 181 +++++++++++ files/es/web/web_components/index.html | 224 ++++++++++++++ .../using_custom_elements/index.html | 304 +++++++++++++++++++ .../web/web_components/using_shadow_dom/index.html | 199 ++++++++++++ .../using_templates_and_slots/index.html | 335 +++++++++++++++++++++ 5 files changed, 1243 insertions(+) create mode 100644 files/es/web/web_components/custom_elements/index.html create mode 100644 files/es/web/web_components/index.html create mode 100644 files/es/web/web_components/using_custom_elements/index.html create mode 100644 files/es/web/web_components/using_shadow_dom/index.html create mode 100644 files/es/web/web_components/using_templates_and_slots/index.html (limited to 'files/es/web/web_components') diff --git a/files/es/web/web_components/custom_elements/index.html b/files/es/web/web_components/custom_elements/index.html new file mode 100644 index 0000000000..4a789f98d2 --- /dev/null +++ b/files/es/web/web_components/custom_elements/index.html @@ -0,0 +1,181 @@ +--- +title: Custom Elements +slug: Web/Web_Components/Custom_Elements +tags: + - Componentes Web + - Web Components + - custom elements +translation_of: Web/Web_Components/Using_custom_elements +--- +

Los Custom Elements son una característica que permite crear tus propios elementos HTML personalizados. Pueden tener un comportamiento personalizado y estilos CSS propios. Son una parte de los Web Components, pero también pueden ser utilizados independientemente.

+ +
+

Nota: Los Custom elements sólo se han estabilizado recientemente, y partes de MDN todavía contienen información desactualizada de las APIs de los antiguos borradores de la especificación.

+
+ +

Puede no estar claro por qué se creó la nueva capacidad de elementos personalizados, ya que ya era posible crear una etiqueta como <mytag> y aplicarle estilo con CSS, luego utilizar scripts para darle comportamiento. Una ventaja que tienen los custom elements son las reacciones de ciclo de vida (lifecycle reactions), que permiten asociar comportamiento a diferentes partes del nuevo "ciclo de vida" del elemento. Por ejemplo, se puede asociar un comportamiento concreto cuando un nuevo elemento se inserta en el DOM ("conectado"), y otro distinto cuando es eliminado del DOM ("desconectado"), o cuando sus atributos cambien.

+ +

El habilitador de claves de los elementos personalizados v1 es el método {{domxref("CustomElementRegistry.define()")}}, el cual se puede utilizar para definir un nuevo elemento personalizado. El nuevo elemento utilizará la clase suministrada para cualquier instancia, en lugar del valor predeterminado  {{domxref("HTMLUnknownElement")}}.  Los elementos personalizados pueden basarse en un elemento nativo como <button>,  utilizando la sintaxis  <button is="my-button">;  estos se llaman elementos integrados personalizados.

+ +

Métodos de custom element

+ +

Los Custom elements tienen los siguientes métodos que dictan su comportamiento:

+ +
+
constructor()
+
Llamado cuando el elemento es creado o actualizado
+
connectedCallback()
+
Llamado cuando el elemento se es insertado en el documento, incluyéndose en un árbol shadow
+
disconnectedCallback()
+
Llamado cuando el elemento es eliminado de un documento
+
attributeChangedCallback(nombreDelAtributo, antiguoValor, nuevoValor, dominio)
+
Llamado cuando un atributo es cambiado, concatenado, eliminado o reemplazado en el elemento. Sólo llamado sobre atributos observados.
+
adoptedCallback(antiguoDocumento, nuevoDocumento)
+
Llamado cuando un elemento es adoptado en otro nuevo documento
+
+ +

Ejemplo

+ +

Los custom elements necesitan usar la sintaxis de clase introducida en las versiones modernas de JavaScript.

+ +

Archivo HTML:

+ +
Si no aparece nada debajo, es que tu navegador no soporta aún los Custom Elements.
+<x-product data-name="Ruby" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/ruby.png" data-url="http://example.com/1"></x-product>
+<x-product data-name="JavaScript" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/javascript.png" data-url="http://example.com/2"></x-product>
+<x-product data-name="Python" data-img="https://s3-us-west-2.amazonaws.com/s.cdpn.io/4621/python.png" data-url="http://example.com/3"></x-product>
+ +

Archivo JS:

+ +
// Declaración de un custom element que hereda de HTMLElement
+class XProduct extends HTMLElement {
+  constructor() {
+    // Siempre debe llamarse primero al constructor padre
+    super();
+
+    // Se crea el shadow root
+    var shadow = this.attachShadow({mode: 'open'});
+
+    // Se crea un elemnto img y se asignan sus atributos.
+    var img = document.createElement('img');
+    img.alt = this.getAttribute('data-name');
+    img.src = this.getAttribute('data-img');
+    img.width = '150';
+    img.height = '150';
+    img.className = 'product-img';
+
+    // Añadir la imagen al shadow root.
+    shadow.appendChild(img);
+
+    // Añadir un elemento de escucha a la imagen.
+    img.addEventListener('click', () => {
+      window.location = this.getAttribute('data-url');
+    });
+
+    // Crear un enlace al producto.
+    var link = document.createElement('a');
+    link.innerText = this.getAttribute('data-name');
+    link.href = this.getAttribute('data-url');
+    link.className = 'product-name';
+
+    // Añadir el enlace al shadow root.
+    shadow.appendChild(link);
+  }
+}
+
+// Definir el nuevo elemento.
+customElements.define('x-product', XProduct);
+
+ +

Archivo CSS:

+ +
body {
+  background: #F7F7F7;
+}
+
+x-product {
+  display: inline-block;
+  float: left;
+  margin: 0.5em;
+  border-radius: 3px;
+  background: #FFF;
+  box-shadow: 0 1px 3px rgba(0,0,0,0.25);
+  font-family: Helvetica, arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+}
+
+x-product::slotted(.product-img) {
+  cursor: pointer;
+  background: #FFF;
+  margin: 0.5em;
+}
+
+x-product::slotted(.product-name) {
+  display: block;
+  text-align: center;
+  text-decoration: none;
+  color: #08C;
+  border-top: 1px solid #EEE;
+  font-weight: bold;
+  padding: 0.75em 0;
+}
+
+ +

A continuación se muestra el ejemplo en vivo de lo anterior:

+ +

{{ EmbedLiveSample('Example', '1500', '250', '', 'Web/Web_Components/Custom_Elements') }}

+ +

Atributos Observados

+ +

Para ser notificado cuando un atributo cambia, se debe definir una lista de atributos observados al inicializar el elemento, poniendo un getter estático observedAttributes en la clase del elemento que devuelve un array de nombre de atributos.

+ +

Archivo JS:

+ +
class HelloElement extends HTMLElement {
+  // Observar los cambios en el atributo 'name'.
+  static get observedAttributes() {return ['name']; }
+
+  // Responder a los cambios en el atributo.
+  attributeChangedCallback(attr, oldValue, newValue) {
+    if (attr == 'name') {
+      this.textContent = `Hello, ${newValue}`;
+    }
+  }
+}
+
+// Definir el nuevo elemento
+customElements.define('hello-element', HelloElement);
+
+ +

Archivo HTML:

+ +
<hello-element name="Anita"></hello-element>
+ +

A continuación está el ejemplo en vivo de lo anterior:

+ +

{{ EmbedLiveSample('Observed_attributes', '750', '100', '', 'Web/Web_Components/Custom_Elements') }}

+ +

Especificaciones

+ +

Los Custom Elements están definido en la siguiente especificación:

+ + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
The HTML Standard: Custom elementsLS 
+ +

Recursos

+ + diff --git a/files/es/web/web_components/index.html b/files/es/web/web_components/index.html new file mode 100644 index 0000000000..96e3250cb2 --- /dev/null +++ b/files/es/web/web_components/index.html @@ -0,0 +1,224 @@ +--- +title: Web Components +slug: Web/Web_Components +tags: + - Componentes + - Componentes Web + - Landing + - TopicStub + - Web Components +translation_of: Web/Web_Components +--- +
{{DefaultAPISidebar("Web Components")}}
+ +
+

Los Componentes Web son un paquete de diferentes tecnologías que te permiten crear elementos personalizados reutilizables — con su funcionalidad encapsulada apartada del resto del código — y utilizarlos en las aplicaciones web.

+
+ +

Conceptos y uso

+ +

Como desarrolladores, sabemos que reutilizar código tanto como sea posible es una buena idea. Esto tradicionalmente no es sencillo para estructuras de marcado personalizadas — piense en el complejo HTML (y sus estilos y scripts asociados) que en ocasiones se han tenido que escribir para renderizar controles de interfaz (UI) personalizados, y ahora usarlos múltiples veces puede crear un caos en la página si no se es cuidadoso.

+ +

Los Componentes Web apuntan a resolver dichos problemas — consiste en tres tecnologías principalmente, las que se pueden usar juntas para crear elementos personalizables versátiles que encapsulan la funcionalidad y pueda ser reutilizada donde sea sin miedo a colisiones de código.

+ + + +

La aproximación básica para implementar un componente web, generalmente es la siguiente:

+ +
    +
  1. Crear una clase o función en la cual especificar la funcionalidad del componente web. Si se usa una clase, usar la sintaxis de ES2015 (ver Clases para más información).
  2. +
  3. Registrar el nuevo elemento personalizado utilizando el método {{domxref("CustomElementRegistry.define()")}}, pasándole el nombre del elemento a ser definido, la clase o función en la cuál su funcionalidad esta especificada, y opcionalmente, de que elemento hereda.
  4. +
  5. Si se requiere, adjuntar un shadow DOM al elemento personalizado usando el método {{domxref("Element.attachShadow()")}}. Añadir elementos hijos, oyentes de eventos, etc., al shadow DOM utilizando métodos normales del DOM.
  6. +
  7. Si se requiere, definir una plantilla HTML utilizando {{htmlelement("template")}} y {{htmlelement("slot")}}. Nuevamente usar métodos regulares del DOM para clonar la plantilla y acoplarla al shadow DOM.
  8. +
  9. Utilizar los componentes personalizados en la página web cuando se desee, como se utilizaría cualquier otro elemento HTML.
  10. +
+ +

Tutoriales

+ +
+
Utilizando elementos personalizados (Using custom elements)
+
Guía que muestra como usar las características de los elementos personalizados para crear componentes web sencillos, así como revisar las llamadas del ciclo de vida y algunas características más avanzadas.
+
Utilizando el shadow DOM
+
Guía que trata los fundamentos del shadow DOM, mostrando como adjuntar un shadow DOM a un elemento, añadir el árbol del shadow DOM, añadirle estilos y más.
+
Usando templates y slots
+
Guía que muestra como definir una estructura HTML reutilizable utilizando elementos {{htmlelement("template")}} y {{htmlelement("slot")}} elements, y entonces usar esa estructura debto del componente web.
+
+ +

Referencia

+ +

Elementos personalizados

+ +
+
{{domxref("CustomElementRegistry")}}
+
Contiene funcionalidad relacionada a los elementos personalizados, más notablemente el método {{domxref("CustomElementRegistry.define()")}} utilizado para registrar nuevos elementos personalizados para que se puedan usar en el documento
+
{{domxref("Window.customElements")}}
+
Retorna una referencia al objeto CustomElementRegistry.
+
Llamadas del ciclo de vida (Life cycle callbacks)
+
Llamadas de funciones especiales declaradas dentro de la clase de definición de los componentes personalizados, los que afectan su comportamiento: +
    +
  • connectedCallback: Invocado cuando el componente personalizado se conecta por primera vez al DOM del documento.
  • +
  • disconnectedCallback: Invocado cuando el componente personalizado se deconecta del DOM del documento.
  • +
  • adoptedCallback: Invocado cuando el componente personalizado se mueve a un nuevo documento.
  • +
  • attributeChangedCallback: Invocado cuando uno de los atributos del componente personalizado es añadido, removido o modificado.
  • +
+
+
+
    +
+
+
+ +
+
Extensiones para crear elementos incorporados personalizados
+
+
    +
  • El atributo global HTML {{htmlattrxref("is")}}: Permite especificar que un elemento estandar HTML debe comportarse como un elemento incorporado personalizado registrado.
  • +
  • La opción "is" del método {{domxref("Document.createElement()")}}: Permite crear una instancia de un elemento HTML estandar que se comporta como un determinado elemento incorporado personalizado registrado.
  • +
+
+
Pseudo-clases CSS
+
Pseudo-clases relacionadas específicamente a elementos personalizados: +
    +
  • {{cssxref(":defined")}}: Coincide con cualquier elemento declarado, incluyendo elementos incorporados y elementos personalizados definidos con CustomElementRegistry.define()).
  • +
  • {{cssxref(":host")}}: Selecciona el shadow host del shadow DOM conteniendo el CSS que es usado.
  • +
  • {{cssxref(":host()")}}: Selecciona el shadow host del shadow DOM conteniendo el CSS que es usado (para que se pueda seleccionar un elemento personalizado desde dentro de su shadow DOM) — pero solo si el selector determinado como el parámetro de la función coincide con el shadow host.
  • +
  • {{cssxref(":host-context()")}}: Selecciona el shadow host del shadow DOM conteniendo el CSS que es usado (para que se pueda seleccionar un elemento personalizado desde dentro de su shadow DOM) — pero solo si el selector determinado como el parámetro de la función coincide con el shadow host de los ancestros del sitio desde el cual esta ubicado en la jerarquía del DOM.
  • +
+
+
+
    +
+
+
Pseudo-elementos CSS
+
Pseudo-elementos relacionados especificamente a elementos personalizados: +
    +
  • {{cssxref("::part")}}: Representa cualquier elemento dentro del shadow tree que contiene un atributo {{HTMLAttrxRef("part")}} que coincida.
  • +
+
+
+ +

Shadow DOM

+ +
+
{{domxref("ShadowRoot")}}
+
Representa el nodo raíz de un subárbol de un shadow DOM.
+
{{domxref("DocumentOrShadowRoot")}}
+
Un mixin definiendo características que son disponibles a través de documentos y shadow roots.
+
Extensiones a {{domxref("Element")}}
+
Extensiones a la interfaz Element relacionada al shadow DOM: +
    +
  • El método {{domxref("Element.attachShadow()")}} conecta un árbol shadow DOM al elemento especificado.
  • +
  • La propiedad {{domxref("Element.shadowRoot")}} retorna el shadow root acoplado al elemento determinado, o null si no hay un shadow root adjuntado.
  • +
+
+
Adiciones relevantes a {{domxref("Node")}}
+
Adiciones a la interfaz Node relevantes al shadow DOM: +
    +
  • El método {{domxref("Node.getRootNode()")}} retorna la raíz del objeto del contexto, que opcionalmente incluye el shadow root si se encuentra disponible.
  • +
  • La propiedad {{domxref("Node.isConnected")}} retorna un boleano indicando si el Nodo esta o no conectado (directamente o indirectamente) al objeto del contexto, es decir, el objeto {{domxref("Document")}} en el caso del DOM normal, o al {{domxref("ShadowRoot")}} en el caso del shadow DOM.
  • +
+
+
Extensiones a {{domxref("Event")}}
+
Extensiones a la interfaz Event relacionada al shadow DOM: +
    +
  • {{domxref("Event.composed")}}: Retorna un {{jsxref("Boolean")}} que indica si el evento se va a propagar a través de los límites del shadow DOM hacia el DOM normal (true), o no (false).
  • +
  • {{domxref("Event.composedPath")}}: Retorna la ruta del evento (objetos en los que oyentes serán invocados). Esto no incluye nodos en shadow trees si el shadow root fue creado con {{domxref("ShadowRoot.mode")}} cerrado.
  • +
+
+
+ +

Plantillas HTML

+ +
+
{{htmlelement("template")}}
+
Contiene un fragmento de HTML que no es renderizado cuando se carga inicialmente un documento que lo contiene, pero puede ser desplegado en tiempo de ejecución usando JavaScript, principalmente usado como la base de la estructura de los elementos personalizados. La interfaz DOM asociada es {{domxref("HTMLTemplateElement")}}.
+
{{htmlelement("slot")}}
+
Un espacio termporal dentro de un componente web que se puede llenar con una plantilla de marcado propia, lo que permite crear árboles DOM separados y presentarlos juntos. La interfaz DOM asociada es {{domxref("HTMLSlotElement")}}.
+
El atributo global HTML slot
+
Asigna un slot en un shadow tree de un shadow DOM shadow tree a un elemento.
+
{{domxref("Slotable")}}
+
Un mixin implementado tanto por los nodos {{domxref("Element")}} y {{domxref("Text")}}, definiendo características que les permiten convertirse en el contenido de un elemento {{htmlelement("slot")}}. El mixin define un atributo, {{domxref("Slotable.assignedSlot")}}, el cual retorna una referencia al nodo del slot donde esta insertado.
+
+ +
+
Extensiones a {{domxref("Element")}}
+
Extensiones a la interfaz Element relacionadas a slots: +
    +
  • {{domxref("Element.slot")}}: Retorna el nombre del slot del shadow DOM adjunto al elemento.
  • +
+
+
Pseudo-elementos de CSS
+
Pseudo-elementos especificamente relacionados a slots: +
    +
  • {{cssxref("::slotted")}}: Coincide cualquier contenido que es insertado dentro de un slot.
  • +
+
+
El evento {{event("slotchange")}}
+
Disparado en una instancia {{domxref("HTMLSlotElement")}} (elemento {{htmlelement("slot")}}) cuando el o los nodos contenidos es ese slot cambia.
+
+ +

Ejemplos

+ +

Se están construyendo varios ejemplos en nuestro repositorio de GitHub web-components-examples. Se añadirán más con el tiempo.

+ +

Especificaciones

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
EspecificaciónEstadoComentario
{{SpecName("HTML WHATWG","scripting.html#the-template-element","<template> element")}}{{Spec2("HTML WHATWG")}}La definición de {{HTMLElement("template")}}.
{{SpecName("HTML WHATWG","custom-elements.html#custom-elements","custom elements")}}{{Spec2("HTML WHATWG")}}La definición de elementos personalizados HTML (HTML Custom Elements).
{{SpecName("DOM WHATWG","#shadow-trees","shadow trees")}}{{Spec2("DOM WHATWG")}}La definición de Shadow DOM.
{{SpecName("HTML Imports", "", "")}}{{Spec2("HTML Imports")}}Definición inicial de HTML Imports.
{{SpecName("Shadow DOM", "", "")}}{{Spec2("Shadow DOM")}}Definición inicial de Shadow DOM.
+ +

Compatibilidad de los navegadores

+ +

Soporte de Componentes Web

+ +

(Imagen tomada de webcomponents.org)

+ +

Para revisar detalladamente el soporte para ciertas características (sobre todo en versiones anteriores o navegadores antiguos), se puede consultar las páginas de referencia listadas anteriormente

+ +

Ver también

+ + diff --git a/files/es/web/web_components/using_custom_elements/index.html b/files/es/web/web_components/using_custom_elements/index.html new file mode 100644 index 0000000000..150d906131 --- /dev/null +++ b/files/es/web/web_components/using_custom_elements/index.html @@ -0,0 +1,304 @@ +--- +title: Usando elementos personalizados +slug: Web/Web_Components/Using_custom_elements +tags: + - Autonomos + - Clases + - Guía + - HTML + - Preconstruidos + - elementos personalizados +translation_of: Web/Web_Components/Using_custom_elements +--- +
{{DefaultAPISidebar("Web Components")}}
+ +

Una de las características claves del estándar de Componentes Web es la capacidad de crear elementos personalizados que encapsulan tu funcionalidad en una página HTML, en vez de tener que hacerlo con una larga lista de elementos anidados que juntos proveen una funcionalidad o característica personalizada en una página. Este artículo presenta el uso del API de Elementos Personalizados.

+ +
+

Nota: Los Elementos Personalizados funcionan por defecto en Firefox, Chrome, y Edge (76). Opera y Safari solo adminten Elementos  Personalizados autónomos (que extienden HTMLElement).

+
+ +

Vista de alto nivel

+ +

El controlador de los elementos personalizados en un documento web es el objeto  {{domxref("CustomElementRegistry")}}  — este objeto te permite registrar un elemento personalizado en la página, devolver información de qué elementos personalizados se han registrado, etc.

+ +

Para registrar un elemento personalizado en la página, debes usar el método {{domxref("CustomElementRegistry.define()")}} . Éste toma los siguientes argumentos:

+ + + +

Entonces por ejemplo, podríamos definir un elemento personalizado word-count como este:

+ +
customElements.define('word-count', WordCount, { extends: 'p' });
+ +

El elemento se llama word-count, la clase es WordCount, y extiende el elemento {{htmlelement("p")}}.

+ +

Para escribir una clase que defina un elemento personalizado, usamos la sintáxis estándar de ES 2015. Por ejemplo, WordCount está estructurada así:

+ +
class WordCount extends HTMLParagraphElement {
+  constructor() {
+    // Siempre llamar primero a super en el constructor
+    super();
+
+    // La funcionalidad del elemento se escribe aquí
+
+    ...
+  }
+}
+ +

Esto es solo un ejemplo sencillo, pero aquí se pueden hacer más cosas. Dentro de la clase podemos definir callbacks de ciclo de vida, que se ejecutan en momentos específicos del ciclo de vida del elemento. Por ejemplo, connectedCallback se invoca cada vez que el elemento se añade a un documento, mientras que attributeChangedCallback se invoca cuando uno de los atributos del elemento se ha añadido, quitado o cambiado.

+ +

Aprenderás más sobre estos callbacks en la sección {{anch("Using the lifecycle callbacks")}} más abajo.

+ +

Hay dos tipos de elementos personalizados :

+ + + +

Trabajando mediante algunos ejemplos sencillos

+ +

Llegados a este punto, veamos con más detalle cómo se crean los elementos personalizados, a través de algunos ejemplos.

+ +

Elementos personalizados autónomos

+ +

Echemos un vistazo al ejemplo de un elemento personalizado autónomo — <popup-info-box> (ver el ejemplo en vivo). Este ejemplo toma un icono y una cadena de texto, y los incrusta en la página. Cuando el icono toma el foco, se muestra el texto en una caja emergente (popup) de información para proporcionar más información de contexto.

+ +

Para empezar, en un fichero JavaScript se define una clase llamada PopUpInfo, que extiende la clase genérica {{domxref("HTMLElement")}}.

+ +
class PopUpInfo extends HTMLElement {
+  constructor() {
+    // Siempre llamar primero a super en el constructor
+    super();
+
+    // La funcionalidad del elemento se escribe aquí
+
+    ...
+  }
+}
+ +

El trozo de código anterior contiene un constructor() para la clase, que siempre empieza llamando a super() de forma que se establezca correctamente el encadenado del prototipo.

+ +

Dentro del constructor, definimos toda la funcionalidad que tendrá el elemento cuando se instancie. En este caso, adjuntamos una shadow root al elemento personalizado, mediante manipulación de DOM creamos la estructura interna del shadow DOM del elemento — que se adjunta a su vez a la shadow root — y finalmente añadimos algo de estilos CSS al shadow root.

+ +
// Creamos un shadow root
+var shadow = this.attachShadow({mode: 'open'});
+
+// Creamos spans
+var wrapper = document.createElement('span');
+wrapper.setAttribute('class','wrapper');
+var icon = document.createElement('span');
+icon.setAttribute('class','icon');
+icon.setAttribute('tabindex', 0);
+var info = document.createElement('span');
+info.setAttribute('class','info');
+
+// Tomamos el contenido del atributo y lo ponemos dentro del span
+var text = this.getAttribute('data-text');
+info.textContent = text;
+
+// Añadimos el icono
+var imgUrl;
+if(this.hasAttribute('img')) {
+  imgUrl = this.getAttribute('img');
+} else {
+  imgUrl = 'img/default.png';
+}
+var img = document.createElement('img');
+img.src = imgUrl;
+icon.appendChild(img);
+
+// Creamos un poco de CSS para aplicar al shadow dom
+var style = document.createElement('style');
+
+style.textContent = '.wrapper {' +
+// CSS truncado por brevedad
+
+// añade los elementos creados al shadow dom
+
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);
+ +

Finalmente, registraremos nuestro elemento en el CustomElementRegistry usando el método define() que mencionamos anteriormente  — en los parámetros especificamos el nombre del elemento, y el nombre de la clase que define su funcionalidad::

+ +
customElements.define('popup-info', PopUpInfo);
+ +

Ahora ya está disponible para usarse en nuestra página. En nuestro HTML, lo usamos de esta manera:

+ +
<popup-info img="img/alt.png" data-text="Your card validation code (CVC)
+  is an extra security feature — it is the last 3 or 4 numbers on the
+  back of your card."></popup-info>
+ +
+

Nota: Puedes ver el código fuente JavaScript completo aquí.

+
+ +
+

Nota: Recuerda que para que los elementos personalizados funcionen, el script que los registra tiene que cargarse después de que el DOM lo interprete.  Esto puede hacerse incluyendo un elemento <script> al final del  <body>, o poniendo el atributo defer en tu elemento <script>.

+
+ +

Estilos internos vs. externos

+ +

En el ejemplo de arriba, aplicamos estilos al Shadow DOM usando el elemento {{htmlelement("style")}} , pero podríamos perfectamente hacerlo referenciando una hoja de estilos externa mediante un elemento {{htmlelement("link")}}.

+ +

Por ejemplo, echemos un vistazo al código de ejemplo de popup-info-box-external-stylesheet (ver el código fuente):

+ +
// Creamos elemento link para cargar hoja de estilos externa
+const linkElem = document.createElement('link');
+linkElem.setAttribute('rel', 'stylesheet');
+linkElem.setAttribute('href', 'style.css');
+
+// Añadimos el elemento creado al shadow dom
+shadow.appendChild(linkElem);
+ +

Nótese que los elementos {{htmlelement("link")}} no bloquean el pintado de shadow root, por lo que podría verse un flash o contenido sin estilo (FOUC)  mientras se carga la hoja de estilos.

+ +

Muchos navegadores modernos implementan una optimización para etiquetas {{htmlelement("style")}} clonadas de un nodo común o que tienen texto idéntico, que les permite compartir una única hoja de estilo por detrás. Con esta optimización, el rendimiento de estilos externos e internos debería ser parecido.

+ +

Elementos preconstruidos personalizados

+ +

Echemos un vistazo ahora a otro ejemplo de elemento preconstruído personalizado — expanding-list (ver el ejemplo en vivo). Este ejemplo convierte cualquier lista sin orden <UL> en un menú expandible/colapsable.

+ +

Primero de todo, definimos nuesta clase, de la misma forma que antes:

+ +
class ExpandingList extends HTMLUListElement {
+  constructor() {
+    // Siempre llamar primero a super en el constructor
+    super();
+
+    // La funcionalidad del elemento se escribe aquí
+
+    ...
+  }
+}
+ +

No explicaremos la funcionalidad del elemento en detalle aquí, pero puedes descubrir cómo funciona mirando el código fuente. La única diferencia real es que nuestro elemento extiende de la interfaz {{domxref("HTMLUListElement")}}, y no de {{domxref("HTMLElement")}}. Por lo que tiene todas las características de un elemento {{htmlelement("ul")}} además de la funcionalidad que agreguemos nosotros, en vez de ser un elemento autónomo. Esto es lo que lo hace un elemento preconstruido personalizado en vez de uno autónomo.

+ +

Después, registramos el elemento usando el método define() como antes, excepto que esta vez incluimos un objeto de opciones, en el tercer parámetro, que detalla de qué elemento hereda:

+ +
customElements.define('expanding-list', ExpandingList, { extends: "ul" });
+ +

El uso de un elemento preconstruido en un documento web también es algo distinto:

+ +
<ul is="expanding-list">
+
+  ...
+
+</ul>
+ +

Usas el elemento <ul> como siempre, pero especificas el nombre del elemento personalizado mediante un atributo  is .

+ +
+

Nota: De nuevo, puedes ver el código fuente JavaScript completo  aquí.

+
+ +

Usando callbacks de ciclo de vida

+ +

Puedes definir varios callbacks dentro de la definición de la clase de un elemento personalizado. Estos callbacks se disparan en distintos puntos del ciclo de vida de elemento:

+ + + +

Echemos un vistazo a un ejemplo de todo esto.  El código de abajo se ha tomado del ejemplo life-cycle-callbacks  (ver el ejemplo en vivo).  Se trata de un ejemplo trivial que simplemente genera un cuadrado coloreado de un tamaño fijo en la página. El elemento personalizado es algo como esto:

+ +
<custom-square l="100" c="red"></custom-square>
+ +

El constructor de la clase es muy simple — adjuntamos un shadow DOM al elemento, y después adjuntamos un {{htmlelement("div")}} vacío y un elemento {{htmlelement("style")}} al shadow root:

+ +
var shadow = this.attachShadow({mode: 'open'});
+
+var div = document.createElement('div');
+var style = document.createElement('style');
+shadow.appendChild(style);
+shadow.appendChild(div);
+ +

La función clave en este ejemplo es  updateStyle() — esta toma un elemento, obtiene su shadow root, busca su elemento <style>, y añade al estilo {{cssxref("width")}}, {{cssxref("height")}}, y {{cssxref("background-color")}}.

+ +
function updateStyle(elem) {
+  const shadow = elem.shadowRoot;
+  shadow.querySelector('style').textContent = `
+    div {
+      width: ${elem.getAttribute('l')}px;
+      height: ${elem.getAttribute('l')}px;
+      background-color: ${elem.getAttribute('c')};
+    }
+  `;
+}
+ +

Las actualizaciones se manejan mediande los callbacks que se incluyen en la definición de la clase. El método connectedCallback() se ejecuta cada vez que el elemento se añade al DOM — en este instante ejecutamos la función updateStyle() para asegurarnos que el cuadrado se pinta tal y como se definió en sus atributos:

+ +
connectedCallback() {
+  console.log('Custom square element added to page.');
+  updateStyle(this);
+}
+ +

Los callbacks disconnectedCallback() y adoptedCallback() simplemente imprimen mensajes a la consola para informarnos cuando el elemento se quita del DOM, o bien se mueve a una página distinta:

+ +
disconnectedCallback() {
+  console.log('Custom square element removed from page.');
+}
+
+adoptedCallback() {
+  console.log('Custom square element moved to new page.');
+}
+ +

El callback attributeChangedCallback() se ejecuta cuando los atributos cambian de alguna forma. Como puedes ver en sus propiedades, es posible actuar sobre atributos individuales, teniendo en cuenta su nombre, y sus valores anterior y nuevo.  Sinembargo en este ejemplo, simplemente ejecutamos de nuevo la función updateStyle() para asegurarnos de que el cuadrado tiene el estilo adecuado a sus nuevos valores:

+ +
attributeChangedCallback(name, oldValue, newValue) {
+  console.log('Custom square element attributes changed.');
+  updateStyle(this);
+}
+ +

Nótese que el callback attributeChangedCallback() se dispara cuando un atributo cambia y está observándose el atributo. Esto se hace mediante el método static get observedAttributes() dentro de la clase  - este debería devolver un array que contiene los nombres de los atributos que se deben observar:

+ +
static get observedAttributes() { return ['c', 'l']; }
+ +

Este código se coloca en la parte de arriba del constructor en nuestro ejemplo.

+ +
+

Nota: Busca el código JavaScript completo aquí.

+
+ +

Polyfills vs. clases

+ +

Polyfills de los elementos personalizados pueden parchear constructores nativos como HTMLElement y otros, y devolver una instancia de una clase diferente de la que se acaba de crear.

+ +

Si necesitas un constructor y una llamada obligatoria a super, recuerda indicar los argumentos opcionales en el constructor y pasarlos a super.

+ +
class CustomElement extends HTMLElement {
+  constructor(...args) {
+    const self = super(...args);
+    // self functionality written in here
+    // self.addEventListener(...)
+    // return the right context
+    return self;
+  }
+}
+ +

Si no necesitar realizar ninguna operación en el constructor, simplemente omítelo, ya que se preservará su comportamiento nativo  (ver a continuación).

+ +
 constructor(...args) { return super(...args); }
+
+ +

Transpiladores vs. clases

+ +

Nótese que las clases ES2015 no pueden transpilarse con Babel 6 o TypeScript para navegadores antiguos. Puedes usar Babel 7 o el plugin  babel-plugin-transform-builtin-classes para Babel 6, y especificar ES2015 como destino (target) en TypeScript en vez de legacy.

+ +

Librerías

+ +

Existen varias librerías que se han construido sobre Web Components con la intención de incrementar el nivel de abstracción cuando se crean elementos personalizados. Algunas de estas librerías son  snuggsi ツX-TagSlim.js, LitElementSmart, y Stencil.

diff --git a/files/es/web/web_components/using_shadow_dom/index.html b/files/es/web/web_components/using_shadow_dom/index.html new file mode 100644 index 0000000000..b660ee7181 --- /dev/null +++ b/files/es/web/web_components/using_shadow_dom/index.html @@ -0,0 +1,199 @@ +--- +title: Usando shadow DOM +slug: Web/Web_Components/Using_shadow_DOM +translation_of: Web/Web_Components/Using_shadow_DOM +--- +
{{DefaultAPISidebar("Web Components")}}
+ +

Un aspecto importante de los componentes Web es la encapsulación — ser capaz de mantener la estructura de marcado, estilo, y comportamiento oculto y separado de otro código en la página para que las diferentes partes no entre en conflicto, y el código pueda permanecer limpio y agradable. El API de DOM Shadow es un parte clave para esto, proporcionando una forma de enlazar un DOM oculto y separado a un elemento. Este artículo cubre los aspectos básicos para utilizar Shadow DOM.

+ +
+

Nota: Shadow DOM es soportado por defecto en Firefox (63 en adelante), Chrome, Opera, y Safari. Edge también está trabajando en una implemetanción.

+
+ +

Vista de alto nivel

+ +

Este artículo asume que usted está familiarizado con el concepto de DOM (Document Object Model) — una estructura en forma de arbol de nodos conectados que representan los diferentes elementos y cadenas de texto que aparecen en un documento de marcado (generalmente un documento HTML en el caso de documentos web). Como ejemplo, considere el siguiente fragmento HTML:

+ +
<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>Simple DOM example</title>
+  </head>
+  <body>
+      <section>
+        <img src="dinosaur.png" alt="A red Tyrannosaurus Rex: A two legged dinosaur standing upright like a human, with small arms, and a large head with lots of sharp teeth.">
+        <p>Here we will add a link to the <a href="https://www.mozilla.org/">Mozilla homepage</a></p>
+      </section>
+  </body>
+</html>
+ +

Este fragmento produce la siguientre estructura de DOM:

+ +

+ +

Shadow DOM permite adjuntar arboles DOM ocultos a elementos en el arbol DOM regular — este arbol shadow DOM comienza con un elemento shadow root, debajo del cual se puede adjuntar cualquier elemento que desee, de la misma manera que el DOM normal.

+ +

+ +

Hay algunos conceptos de Shadow DOM que deben ser tomados en cuenta:

+ + + +

Puede manipular los nodos del 'shadow DOM' de la misma manera que los nodos del arbol DOM regular. Por ejemplo, agregando hijos o estableciendo atributos, dando estilo a nodos individuales utilizando element.style.foo, o agregando estilo a todo el árbol de 'shadow DOM' dentro del elemento <style>. La diferencia es que nada del código dentro de un 'shadow DOM' puede afectar a nada fuera de él, lo que permite una encapsulación práctica.

+ +

Cabe destacar que el shadow DOM no es algo nuevo — los exploradores lo han usado por un largo tiempo para encapsular la estructura interna de un elemento. Piensa por ejemplo en un elemento {{htmlelement("video")}}, con los controles predeterminados del explorador a la vista. Todo lo que ves en el DOM es el elemento <video>, pero este contiene una serie de botones y otros controles dentro de su shadow DOM. Las especificaciones del shadow DOM fueron hechas para que seas capaz de manipular el shadow DOM de tus elementos personalizados.

+ +

Uso básico

+ +

Puede adjuntar un 'shadow root' a cualquier elemento utilizando el método {{domxref ("Element.attachShadow ()")}}. Éste toma como parámetro un objeto que contiene una propiedad — modo — con dos posibles valores: 'open' o 'closed'.

+ +
let shadow = elementRef.attachShadow({mode: 'open'});
+let shadow = elementRef.attachShadow({mode: 'closed'});
+ +

open siginifica que puede acceder al shadow DOM usando JavaScript en el contexto principal de la página. Por ejemplo, usando la propiedad {{domxref("Element.shadowRoot")}}:

+ +
let myShadowDom = myCustomElem.shadowRoot;
+ +

If you attach a shadow root to a custom element with mode: closed set, you won't be able to access the shadow DOM from the outside — myCustomElem.shadowRoot returns null. This is the case with built in elements that contain shadow DOMs, such as <video>.

+ +
+

Note: As this blog post shows, it is actually fairly easy to work around closed shadow DOMs, and the hassle to completely hide them is often more than it's worth.

+
+ +

If you are attaching a shadow DOM to a custom element as part of its constructor (by far the most useful application of the shadow DOM), you would use something like this:

+ +
let shadow = this.attachShadow({mode: 'open'});
+ +

When you've attached a shadow DOM to an element, manipulating it is a matter of just using the same DOM APIs as you use for the regular DOM manipulation:

+ +
var para = document.createElement('p');
+shadow.appendChild(para);
+// etc.
+ +

Working through a simple example

+ +

Now let's walk through a simple example to demonstrate the shadow DOM in action inside a custom element — <popup-info-box> (see a live example also). This takes an image icon and a text string, and embeds the icon into the page. When the icon is focused, it displays the text in a pop up information box to provide further in-context information. To begin with, in our JavaScript file we define a class called PopUpInfo, which extends HTMLElement:

+ +
class PopUpInfo extends HTMLElement {
+  constructor() {
+    // Always call super first in constructor
+    super();
+
+    // write element functionality in here
+
+    ...
+  }
+}
+ +

Inside the class definition we define the element's constructor, which defines all the functionality the element will have when an instance of it is instantiated.

+ +

Creating the shadow root

+ +

We first attach a shadow root to the custom element:

+ +
// Create a shadow root
+var shadow = this.attachShadow({mode: 'open'});
+ +

Creating the shadow DOM structure

+ +

Next, we use some DOM manipulation to create the element's internal shadow DOM structure:

+ +
// Create spans
+var wrapper = document.createElement('span');
+wrapper.setAttribute('class','wrapper');
+var icon = document.createElement('span');
+icon.setAttribute('class','icon');
+icon.setAttribute('tabindex', 0);
+var info = document.createElement('span');
+info.setAttribute('class','info');
+
+// Take attribute content and put it inside the info span
+var text = this.getAttribute('text');
+info.textContent = text;
+
+// Insert icon
+var imgUrl;
+if(this.hasAttribute('img')) {
+  imgUrl = this.getAttribute('img');
+} else {
+  imgUrl = 'img/default.png';
+}
+var img = document.createElement('img');
+img.src = imgUrl;
+icon.appendChild(img);
+
+ +

Styling the shadow DOM

+ +

After that we create a {{htmlelement("style")}} element and populate it with some CSS to style it:

+ +
// Create some CSS to apply to the shadow dom
+var style = document.createElement('style');
+
+style.textContent = `
+.wrapper {
+  position: relative;
+}
+
+.info {
+  font-size: 0.8rem;
+  width: 200px;
+  display: inline-block;
+  border: 1px solid black;
+  padding: 10px;
+  background: white;
+  border-radius: 10px;
+  opacity: 0;
+  transition: 0.6s all;
+  position: absolute;
+  bottom: 20px;
+  left: 10px;
+  z-index: 3;
+}
+
+img {
+  width: 1.2rem;
+}
+
+.icon:hover + .info, .icon:focus + .info {
+  opacity: 1;
+}`;
+
+
+ +

Attaching the shadow DOM to the shadow root

+ +

The final step is to attach all the created elements to the shadow root:

+ +
// attach the created elements to the shadow dom
+shadow.appendChild(style);
+shadow.appendChild(wrapper);
+wrapper.appendChild(icon);
+wrapper.appendChild(info);
+ +

Using our custom element

+ +

Once the class is defined, using the element is as simple as defining it, and putting it on the page, as explained in Using custom elements:

+ +
// Define the new element
+customElements.define('popup-info', PopUpInfo);
+ +
<popup-info img="img/alt.png" text="Your card validation code (CVC) is an extra
+                                    security feature — it is the last 3 or 4
+                                    numbers on the back of your card.">
+ +
+

See also

+ + +
diff --git a/files/es/web/web_components/using_templates_and_slots/index.html b/files/es/web/web_components/using_templates_and_slots/index.html new file mode 100644 index 0000000000..d8047d325b --- /dev/null +++ b/files/es/web/web_components/using_templates_and_slots/index.html @@ -0,0 +1,335 @@ +--- +title: Usando plantillas y slots +slug: Web/Web_Components/Using_templates_and_slots +tags: + - Componentes Web + - Template + - shadow dom + - slot +translation_of: Web/Web_Components/Using_templates_and_slots +--- +
{{DefaultAPISidebar("Web Components")}}
+ +

Este artículo explica como puedes usar los elementos {{htmlelement("template")}} y {{htmlelement("slot")}} para crear una plantilla flexible que luego puede ser usada para rellenar el shadow DOM de un componente web.

+ +

La verdad acerca del elemento <template>

+ +

Cuando tienes que reutilizar las mismas estructuras de lenguaje de marcado repetidas veces en una página web, tiene sentido utilizar algún tipo de plantilla en lugar de repetir la misma estructura una y otra vez. Esto ya era posible hacerlo antes, pero ahora es mucho mas fácil con el elemento HTML {{htmlelement("template")}} (que está bien soportado en los navegadores modernos). Este elemento y su contenido no son renderizados en el DOM, pero pueden ser referenciados vía JavaScript.

+ +

Echemos un vistazo a un ejemplo sencillo:

+ +
<template id="my-paragraph">
+  <p>Mi párrafo</p>
+</template>
+ +

Esto no aparecerá en tu página hasta que hagas una referencia a él con JavaScript y luego lo agregues al DOM, usando algo parecido a lo siguiente:

+ +
let template = document.getElementById('my-paragraph');
+let templateContent = template.content;
+document.body.appendChild(templateContent);
+ +

Aunque de una manera simple, ya puedes empezar a ver su utilidad.

+ +

Usando el elemento <template> con componentes web

+ +

Las plantillas son útiles por si mismas, pero trabajan aún mejor con componentes web. Definamos un componente web que use nuestra plantilla como el contenido de su shadow DOM. Lo nombraremos <my-paragraph>:

+ +
customElements.define('my-paragraph',
+  class extends HTMLElement {
+    constructor() {
+      super();
+      let template = document.getElementById('my-paragraph');
+      let templateContent = template.content;
+
+      const shadowRoot = this.attachShadow({mode: 'open'})
+        .appendChild(templateContent.cloneNode(true));
+  }
+})
+ +

El punto clave a tener en cuenta aquí es que agregamos un clon del contenido de la plantilla al shadow root creado usando el método {{domxref("Node.cloneNode()")}}.

+ +

Y debido a que estamos agregando su contenido a un shadow DOM, podemos incluir cierta información de estilo dentro de la plantilla en un elemento {{htmlelement("style")}}, que luego se encapsula dentro del elemento personalizado. Esto no funcionaría si nosotros solo lo agregásemos al DOM estandar.

+ +

Por ejemplo:

+ +
<template id="my-paragraph">
+  <style>
+    p {
+      color: white;
+      background-color: #666;
+      padding: 5px;
+    }
+  </style>
+  <p>Mi párrafo</p>
+</template>
+ +

Ahora podemos usarlo simplemente agregándolo a nuestro documento HTML:

+ +
<my-paragraph></my-paragraph>
+
+ +
+

Nota: Las plantillas están bien soportadas en los navegadores: la API del Shadow DOM  es compatible por defecto con Firefox (version 63 en adelante), Chrome, Opera y Safari, Edge está trabajando en una implementación.

+
+ +

Añadiendo flexibilidad con el elemento <slot>

+ +

Hasta aquí bien, pero el elemento no es muy flexible. Solo podemos mostrar una parte de texto dentro de él, lo que quiere decir que, hasta el momento, es menos útil que un párrafo normal. Podemos mostrar diferente texto en cada instancia de elemento de una forma declarativa agradable usando el elemento {{htmlelement("slot")}}. Este tiene un soporte más limitado que el elemento {{htmlelement("template")}}, disponible desde Chrome 53, Opera 40, Safari 10, Firefox 59, pero aún no disponible en Edge.

+ +

Los slots son identificados por su atributo name, y te permiten definir marcadores de posición en tu plantilla que pueden rellenarse con cualquier fragmento de marcado cuando el elemento es usado.

+ +

Así que, si queremos agregar un slot dentro de nuestro ejemplo sencillo, podemos actualizar el elemento de párrafo de la plantilla de la siguiente manera:

+ +
<p><slot name="my-text">Mi texto predeterminado</slot></p>
+ +

Si el contenido del slot no está definido cuando el elemento se agrega al marcado, o si el navegador no soporta el elemento slot, <my-paragraph> solo contiene el texto alternativo "Mi texto predeterminado".

+ +

Para definir el contenido de un slot, incluimos una estructura HTML dentro del elemento <my-paragraph> con un atributo {{htmlattrxref("slot")}} cuyo valor es igual al nombre del slot que  queremos rellenar. Al igual que antes, esto puede ser cualquier cosa, por ejemplo:

+ +
<my-paragraph>
+  <span slot="my-text">¡Tengamos un texto diferente!</span>
+</my-paragraph>
+ +

o

+ +
<my-paragraph>
+  <ul slot="my-text">
+    <li>¡Tengamos un texto diferente!</li>
+    <li>¡En una lista!</li>
+  </ul>
+</my-paragraph>
+
+ +
+

Nota: Los elementos que pueden ser insertados en los slots son conocidos como {{domxref("Slotable")}}; cuando un elemento ha sido insertado en un slot, se dice que fue eslotado por su término en inlgés slotted.

+
+ +
+

Nota: Un {{HTMLElement("slot")}} sin nombre se rellenará con todos los nodos secundarios de nivel superior del elemento personalizado que no tengan el atributo {{htmlattrxref("slot")}}. Esto incluye nodos de texto.

+
+ +

Y eso es todo nuestro ejemplo sencillo. Si quieres jugar con él un poco más, puedes encontrarlo en GitHub (también puedes verlo en vivo).

+ +

Un ejemplo más completo

+ +

Para finalizar el artículo, veamos algo menos trivial.

+ +

El siguiente conjunto de fragmentos de código muestra cómo usar {{HTMLElement("slot")}} junto con  {{HTMLElement("template")}} y algo de JavaScript para:

+ + + +

Observa que es técnicamente posible usar el elemento {{HTMLElement("slot")}} sin un elemento {{HTMLElement("template")}}, por ejemplo, dentro de un elemento {{HTMLElement("div")}} normal, y aun así tomar ventaja de los indicadores de posición de el elemento {{HTMLElement("slot")}} para el contenido del Shadow DOM, y al hacerlo puedes evitar el problema de tener que acceder primero a la propiedad content del elemento de la plantilla y clonarlo. Sin embargo, por lo general, es más práctico agregar slots dentro de un elemento {{HTMLElement("template")}}, ya que es poco probable que necesites definir un patrón basado en un elemento ya renderizado.

+ +

Además, incluso si no está renderizado, el propósito del contenedor como plantilla debería ser semánticamente más claro cuando se usa el elemento {{HTMLElement("template")}}. Además, el elemento {{HTMLElement("template")}} puede tener elementos agregados directamente a él, como {{HTMLElement("td")}}, que desaparecerían al añadirse a un {{HTMLElement ("div")}}.

+ +
+

Nota: Puedes encontrar el ejemplo completo en element-details (también lo puedes ver en vivo)

+
+ +

Creando una plantilla con algunos elementos <slot>

+ +

En primer lugar, usamos el elemento {{HTMLElement("slot")}} dentro de un elemento {{HTMLElement("template")}} para crear un nuevo fragmento de documento de tipo "element-details-template" que contiene algunos slots con el atributo name:

+ +
<template id="element-details-template">
+  <style>
+  details {font-family: "Open Sans Light",Helvetica,Arial}
+  .name {font-weight: bold; color: #217ac0; font-size: 120%}
+  h4 { margin: 10px 0 -8px 0; }
+  h4 span { background: #217ac0; padding: 2px 6px 2px 6px }
+  h4 span { border: 1px solid #cee9f9; border-radius: 4px }
+  h4 span { color: white }
+  .attributes { margin-left: 22px; font-size: 90% }
+  .attributes p { margin-left: 16px; font-style: italic }
+  </style>
+  <details>
+    <summary>
+      <span>
+        <code class="name">&lt;<slot name="element-name">NECESITA NOMBRE</slot>&gt;</code>
+        <i class="desc"><slot name="description">NECESITA DESCRIPCIÓN</slot></i>
+      </span>
+    </summary>
+    <div class="attributes">
+      <h4><span>Atributos</span></h4>
+      <slot name="attributes"><p>Ninguno</p></slot>
+    </div>
+  </details>
+  <hr>
+</template>
+
+ +

Ese elemento {{HTMLElement("template")}} tiene varias características.

+ + + +

Crear un nuevo elemento <element-details> desde el elemento <template>

+ +

A continuación, crearemos un nuevo elemento personalizado llamado <element-details> y usaremos {{DOMXref("Element.attachShadow")}} para anclarlo, como su shadow root, a ese fragmento de documento que creamos anteriormente con nuestro elemento {{HTMLElement("template")}}. Usa exactamente el mismo patrón que vimos antes en el ejemplo sencillo.

+ +
customElements.define('element-details',
+  class extends HTMLElement {
+    constructor() {
+      super();
+      var template = document
+        .getElementById('element-details-template')
+        .content;
+      const shadowRoot = this.attachShadow({mode: 'open'})
+        .appendChild(template.cloneNode(true));
+  }
+})
+
+ +

Usando el elemento <element-details> con slots con el atributo name

+ +

Ahora tomaremos el elemento <element-details> para usarlo en nuestro documento.

+ +
<element-details>
+  <span slot="element-name">slot</span>
+  <span slot="description">Un marcador de posición dentro de un
+     componente web que los usuarios pueden rellenar con su propio marcado,
+     con el efecto de componer diferentes árboles DOM
+     juntos.</span>
+  <dl slot="attributes">
+    <dt>name</dt>
+    <dd>El atributo name del slot.</dd>
+  </dl>
+</element-details>
+
+<element-details>
+  <span slot="element-name">template</span>
+  <span slot="description">Un mecanismo para guardar contenido
+     en el lado cliente que no se renderiza cuando la página se
+     carga sino que posteriormente se puede instanciar en
+     tiempo de ejecución usando JavaScript.</span>
+</element-details>
+
+ +

Observa estos puntos sobre el fragmento anterior:

+ + + +

Añadamos algunos estilos

+ +

Como toque final, añadiremos algunos estilos CSS a los ellementos {{HTMLElement("dl")}}, {{HTMLElement("dt")}}, y {{HTMLElement("dd")}} en el documento:

+ +
  dl { margin-left: 6px; }
+  dt { font-weight: bold; color: #217ac0; font-size: 110% }
+  dt { font-family: Consolas, "Liberation Mono", Courier }
+  dd { margin-left: 16px }
+
+ + + +

Resultado

+ +

Finalmente, juntemos todos los fragmentos y veamos cómo se ve el resultado renderizado.

+ +

{{ EmbedLiveSample('full_example', '300','400','https://mdn.mozillademos.org/files/14553/element-details.png','') }}

+ +

Observa los siguientes puntos del resultado renderizado:

+ + + + -- cgit v1.2.3-54-g00ecf