Compatibilidad con las capas superiores en las Herramientas para desarrolladores de Chrome

Alina Varkki
Alina Varkki

Las herramientas para desarrolladores de Chrome están agregando compatibilidad con los elementos de las capas superiores, lo que permite que los desarrolladores depuren con mayor facilidad su código que utiliza esos elementos.

En este artículo, se describe qué son los elementos de la capa superior, cómo las Herramientas para desarrolladores ayudan a visualizar el contenido de la capa superior para comprender y depurar la estructura del DOM que contiene los elementos de la capa superior, y cómo se implementa la compatibilidad con esas capas.

¿Cuáles son la capa superior y los elementos de la capa superior?

¿Qué sucede exactamente internamente cuando abres un <dialog> como modal? 🤔

Se coloca en una capa superior. El contenido de la capa superior se renderiza sobre el resto del contenido. Por ejemplo, debe aparecer un diálogo modal encima de todo el contenido del DOM, por lo que el navegador renderiza automáticamente este elemento en una "capa superior" en lugar de obligar a los autores a luchar manualmente contra el índice z. Un elemento de la capa superior aparece sobre un elemento incluso con el índice z más alto.

La capa superior se puede describir como "la capa de apilamiento más alta". Cada documento tiene un solo viewport asociado y, por lo tanto, también una sola capa superior. Varios elementos pueden estar dentro de la capa superior al mismo tiempo. Cuando eso sucede, se apilan una sobre otra y la última está en la parte superior. En otras palabras, todos los elementos de la capa superior se colocan en una pila del último en entrar, primero en salir (LIFO), en la capa superior.

El elemento <dialog> no es el único que el navegador renderiza en una capa superior. Actualmente, los elementos de la capa superior son: ventanas emergentes, diálogos modales y elementos en modo de pantalla completa.

Examina la siguiente implementación del diálogo:

<main>
  <button onclick="window.dialog.showModal();">Open Dialog</button>
</main>
<dialog id="dialog"></dialog>

Aquí hay una demostración con un par de diálogos que tienen estilos aplicados a sus fondos (los fondos se describen a continuación):

¿Qué es un fondo?

Por suerte, hay una forma de personalizar el contenido debajo del elemento de la capa superior.

Cada elemento de la capa superior tiene un seudoelemento CSS llamado fondo.

El fondo es un cuadro del tamaño de la vista del puerto que se renderiza inmediatamente debajo de cualquier elemento de la capa superior. El seudoelemento ::backdrop permite oscurecer, ocultar o personalizar por completo todo lo que se encuentra debajo del elemento cuando se encuentra en la parte superior de la capa superior.

Cuando estableces varios elementos modal, el navegador dibuja el fondo inmediatamente debajo del elemento más frontal y sobre otros elementos de pantalla completa.

Así es como diseñas un fondo:

/* The browser displays the backdrop only when the dialog.showModal() function opens the dialog.*/
dialog::backdrop {
    background: rgba(255,0,0,.25);
}

¿Cómo se muestra solo el primer fondo?

Cada elemento de la capa superior tiene un fondo que pertenece a una pila de capa superior. Estos fondos están diseñados para superponerse entre sí, por lo que si la opacidad de un fondo no es del 100%, los fondos debajo están visibles.

Si solo debe estar visible el primer fondo de la pila de la capa superior, puedes realizar un seguimiento de los identificadores de elementos en la pila de la capa superior.

Si el elemento agregado no es el primero en la capa superior, la función a la que se llama cuando el elemento se coloca en la capa superior aplica una clase hiddenBackdrop a ::backdrop. Esta clase se quita cuando el elemento se quita de la capa superior.

Revisa el código en esta demostración de ejemplo:

Diseño de compatibilidad con capas superiores en Herramientas para desarrolladores

La compatibilidad de las Herramientas para desarrolladores con la capa superior ayuda a los desarrolladores a comprender el concepto de la capa superior y a visualizar cómo cambia el contenido de la capa superior. Estas funciones ayudan a los desarrolladores a identificar lo siguiente:

  • Los elementos de la capa superior en cualquier momento y su orden
  • Es el elemento que está en la parte superior de la pila en cualquier momento.

Además, la compatibilidad con la capa superior de Herramientas para desarrolladores ayuda a visualizar la posición del seudoelemento de fondo en la pila de la capa superior. Si bien no es un elemento de árbol, desempeña un papel importante en el funcionamiento de la capa superior y puede ser útil para los desarrolladores.

Con las funciones de compatibilidad de la capa superior, puedes hacer lo siguiente:

  1. Observa qué elementos se encuentran en la pila de la capa superior en cualquier momento. La pila de representación de la capa superior cambia de manera dinámica a medida que se agregan o quitan elementos de la capa superior.
  2. Observa la posición del elemento en la pila de la capa superior.
  3. Salta desde el pseudoelemento de fondo del elemento de la capa superior o los elementos en el árbol hasta el elemento o el pseudoelemento de fondo en el contenedor de representación de la capa superior y regresa.

Veamos cómo usar estas funciones.

Contenedor de la capa superior

Para ayudar a visualizar los elementos de la capa superior, Herramientas para desarrolladores agrega un contenedor de capa superior al árbol de elementos. Se encuentra después de la etiqueta de cierre </html>.

Este contenedor te permite observar los elementos de la pila de la capa superior en cualquier momento. El contenedor de la capa superior es una lista de vínculos a los elementos de la capa superior y sus fondos. La pila de representación de la capa superior cambia de manera dinámica a medida que se agregan o quitan elementos de la capa superior.

Para encontrar los elementos de la capa superior en el árbol de elementos o en el contenedor de la capa superior, haz clic en los vínculos de la representación del elemento de la capa superior en el contenedor de la capa superior al mismo elemento en el árbol de elementos y viceversa.

Para saltar del elemento del contenedor de la capa superior al elemento del árbol de la capa superior, haz clic en el botón revelar que se encuentra junto al elemento en el contenedor de la capa superior.

Saltar del vínculo del contenedor de la capa superior al elemento.

Para saltar del elemento del árbol de la capa superior al vínculo del contenedor de la capa superior, haz clic en la insignia de la capa superior que se encuentra junto al elemento.

Saltar de un elemento al vínculo del contenedor de la capa superior.

Puedes desactivar cualquier insignia, incluida la de capa superior. Para inhabilitar las insignias, haz clic con el botón derecho en cualquier insignia, selecciona Configuración de insignias y desmarca las marcas que aparecen junto a las insignias que quieres ocultar.

Desactivando la insignia.

Orden de los elementos en la pila de la capa superior

El contenedor de la capa superior muestra los elementos tal como aparecen en la pila, pero en orden inverso. La parte superior del elemento de la pila es el último en la lista de elementos del contenedor de la capa superior. Esto significa que el último elemento de la lista de contenedores de la capa superior es el elemento con el que actualmente puedes interactuar en el documento.

Las insignias junto a los elementos del árbol indican si los elementos pertenecen a la capa superior y si contienen el número de posición de un elemento en la pila.

En esta captura de pantalla, la pila de la capa superior consta de dos elementos, con el segundo elemento en la parte superior. Si quitas el segundo elemento, el primero se moverá a la parte superior.

El orden de los elementos en la pila.

Fondos en el contenedor de la capa superior

Como se mencionó anteriormente, cada elemento de la capa superior tiene un seudoelemento CSS llamado fondo. Puedes darle estilo a este elemento, por lo que es útil inspeccionarlo y ver su representación.

En el árbol de elementos, un elemento de fondo reside antes de la etiqueta de cierre del elemento al que pertenece. Sin embargo, en el contenedor de la capa superior, aparece un vínculo de fondo justo encima del elemento de la capa superior al que pertenece.

Posición de la pila de fondos.

Cambios en el árbol del DOM

ElementsTreeElement, la clase responsable de crear y administrar elementos individuales del árbol del DOM en Herramientas para desarrolladores, no fue suficiente para implementar un contenedor de capa superior.

Para mostrar el contenedor de la capa superior como un nodo en el árbol, agregamos una nueva clase que crea nodos de elementos de árbol de Herramientas para desarrolladores. Anteriormente, la clase responsable de crear un árbol de elementos de Herramientas para desarrolladores inicializado cada TreeElement con una DOMNode, que es una clase con un backendNodeId y otras propiedades relacionadas con el backend. backendNodeId, a su vez, se asigna en el backend.

El nodo contenedor de la capa superior, que tiene una lista de vínculos a los elementos de la capa superior, debe comportarse como un nodo de elemento de árbol normal. Sin embargo, este nodo no es un nodo del DOM “real” y no es necesario que el backend cree el nodo contenedor de la capa superior.

Para crear un nodo de frontend que represente la capa superior, agregamos un nuevo tipo de nodo de frontend que se crea sin una DOMNode. Este elemento de contenedor de la capa superior es el primer nodo de frontend que no tiene un DOMNode, lo que significa que solo existe en el frontend y el backend no lo "conoce". Para tener el mismo comportamiento que otros nodos, creamos una nueva clase TopLayerContainer que extiende la clase UI.TreeOutline.TreeElement, que es responsable del comportamiento de los nodos frontend.

Para lograr la posición deseada, la clase que renderiza un elemento adjunta TopLayerContainer como el siguiente elemento del mismo nivel de la etiqueta <html>.

Una nueva insignia de capa superior indica que el elemento se encuentra en la capa superior y sirve como vínculo al acceso directo de este elemento en el elemento TopLayerContainer.

Diseño inicial

Al principio, el plan era duplicar los elementos de la capa superior en el contenedor de la capa superior en lugar de crear una lista de enlaces a los elementos. No implementamos esta solución debido a la forma en que funciona la recuperación de los elementos secundarios del elemento en Herramientas para desarrolladores. Cada elemento tiene un puntero superior que se usa para recuperar elementos secundarios, y es imposible tener varios punteros. Por lo tanto, no podemos tener un nodo que se expanda correctamente y contenga todos los elementos secundarios en varios lugares del árbol. En general, el sistema no se creó teniendo en cuenta los subárboles duplicados.

La vulneración a la que llegamos fue crear vínculos a los nodos del DOM del frontend, en lugar de duplicar esos nodos. La clase responsable de crear vínculos a elementos en Herramientas para desarrolladores es ShortcutTreeElement, que extiende UI.TreeOutline.TreeElement. ShortcutTreeElement tiene el mismo comportamiento que otros elementos del árbol del DOM de Herramientas para desarrolladores, pero no tiene un nodo correspondiente en el backend y tiene un botón que se vincula a un ElementsTreeElement. Cada ShortcutTreeElement del nodo de la capa superior tiene un ShortcutTreeElement secundario que se vincula a la representación de un seudoelemento ::backdrop en el árbol del DOM de Herramientas para desarrolladores.

Diseño inicial:

Diseño inicial

Cambios en el protocolo de Herramientas para desarrolladores de Chrome (CDP)

Para implementar la compatibilidad con la capa superior, se deben realizar cambios en el protocolo para Herramientas para desarrolladores de Chrome (CDP). CDP funciona como un protocolo de comunicación entre Herramientas para desarrolladores y Chromium.

Debemos agregar lo siguiente:

  • Un comando para llamar desde el frontend en cualquier momento
  • Un evento para activar en el frontend desde el backend

CDP: Comando DOM.getTopLayerElements

Para mostrar los elementos actuales de la capa superior, necesitamos un nuevo comando de CDP experimental que muestra una lista de los IDs de nodo de los elementos que se encuentran en la capa superior. Herramientas para desarrolladores llama a este comando cada vez que se abre o cuando los elementos de la capa superior cambian. El comando se ve de la siguiente manera:

  # Returns NodeIds of the current top layer elements.
  # Top layer renders closest to the user within a viewport, therefore, its elements always
  # appear on top of all other content.
  experimental command getTopLayerElements
    returns
      # NodeIds of the top layer elements.
      array of NodeId nodeIds

CDP: DOM.topLayerElementsUpdated evento

Para obtener la lista actualizada de los elementos de la capa superior, necesitamos que cada cambio de estos elementos active un evento de CDP experimental. Este evento informa al frontend del cambio que luego llama al comando DOM.getTopLayerElements y recibe la nueva lista de elementos.

El evento tiene el siguiente aspecto:

  # Called by the change of the top layer elements.
  experimental event topLayerElementsUpdated

Consideraciones de CDP

Había varias opciones para implementar la compatibilidad de CDP de la capa superior. Otra opción que tuvimos en cuenta era crear un evento que devolviera la lista de los elementos de la capa superior en lugar de informar al frontend sobre la adición o eliminación de un elemento de la capa superior.

Como alternativa, podríamos crear dos eventos en lugar del comando: topLayerElementAdded y topLayerElementRemoved. En este caso, recibiríamos un elemento y tendríamos que administrar el array de los elementos de la capa superior en el frontend.

Actualmente, un evento de frontend llama al comando getTopLayerElements para obtener una lista de los elementos actualizados. Si enviáramos una lista de elementos o un elemento específico que provocó el cambio cada vez que se activa un evento, podríamos evitar un paso de llamar al comando. Sin embargo, en este caso, el frontend perdería el control sobre los elementos que se envían.

Lo implementamos de esta manera porque, en nuestra opinión, es mejor si el frontend decide cuándo solicitar nodos de capa superior. Por ejemplo, si la capa superior se contrae en la IU o el usuario usa un panel de Herramientas para desarrolladores que no tiene el árbol de elementos, no es necesario obtener los nodos adicionales que podrían estar más profundos en el árbol.