DEV Community

Franklin
Franklin

Posted on

La mejor arquitectura HTML empieza por decidir qué no debe tocar jamás JavaScript

Hermano estructural de La mejor arquitectura CSS empieza por decidir qué no debe hacer jamás CSS — misma distinción, capa diferente.

También disponible en Inglés

El problema

Un ticket de soporte: la página de un artículo en el centro de ayuda se ve exactamente bien. La sección de preguntas frecuentes se renderiza, cada pregunta es visible, el maquetado coincide con el diseño. Hacer clic en una pregunta no hace nada. Ningún error que un usuario pueda ver jamás, ninguna imagen rota, ningún texto faltante. El botón está ahí, estilizado correctamente, y presionarlo no cambia nada en absoluto.

El estado abierto y cerrado del componente nunca estuvo representado en un lugar donde el navegador pudiera actuar sin ayuda. Existía únicamente en un alternador de clases en JavaScript, vinculado una sola vez durante la inicialización. Algo anterior en esa inicialización nunca terminó de ejecutarse. El marcado enviado estaba completo. El mecanismo que debía hacerlo interactivo nunca llegó.

Por qué existe el problema

Durante mucho tiempo, la plataforma no ofreció un modelo de interacción nativo para desplegables. Por lo tanto, los desarrolladores debieron construir uno propio: un control, una representación de la visibilidad y código para coordinar la transición entre ambos. JavaScript se convirtió en el lugar habitual para sostener esa lógica de interacción, y la arquitectura sobrevivió después de que la plataforma incorporara una alternativa nativa.

El problema es que la razón de esa arquitectura desapareció casi por completo, pero la arquitectura permaneció.

El primer principio

El navegador no necesita el permiso de JavaScript para saber si algo está abierto. Cuando ya existe un estado de interacción nativo, la plataforma es dueña de ese estado y de sus transiciones impulsadas por el usuario, sin requerir que el JavaScript de la aplicación las implemente. JavaScript aún puede participar —leyendo el estado, respondiendo a él o modificándolo de forma programática cuando la aplicación realmente lo requiera— sin ser la razón por la que la interacción funciona.

Es una afirmación más acotada de lo que parece. dialog.showModal() y details.open = true son casos de JavaScript tocando el estado nativo directamente, y ninguno de los dos viola el principio. La distinción no es si JavaScript tiene permitido estar cerca de una interacción. Es si JavaScript es la única razón por la que la interacción funciona.

Demostración del principio

Un desplegable individual no necesita nada de JavaScript:

<details>
  <summary>What is HTML?</summary>
  <p>A markup language for describing the structure of a document.</p>
</details>
Enter fullscreen mode Exit fullscreen mode

Haz clic en el resumen y el navegador lo abre. Haz clic de nuevo y se cierra. Sin clases, sin escuchadores, sin estado que inicializar. El atributo open es el estado, y el navegador ya sabe cómo cambiarlo.

La versión más compleja de este patrón —la que los desarrolladores históricamente recreaban con JavaScript— es el acordeón exclusivo: un grupo donde abrir un elemento cierra los demás. Esa coordinación solía requerir rastrear el estado de cada elemento de forma centralizada y cerrar el resto a mano en cada clic. La plataforma ahora también es dueña de eso:

<details name="faq">
  <summary>What is HTML?</summary>
  <p>A markup language for describing the structure of a document.</p>
</details>

<details name="faq">
  <summary>What is CSS?</summary>
  <p>A language for describing how that structure is presented.</p>
</details>

<details name="faq">
  <summary>What is JavaScript?</summary>
  <p>A language for adding behavior on top of both.</p>
</details>
Enter fullscreen mode Exit fullscreen mode

Cada <details> que comparte el mismo name se integra a un grupo exclusivo. Abrir uno cierra cualquiera de los otros que estuviera abierto, exactamente igual a como se comportan las entradas de tipo radio que comparten nombre. Nada coordina esto desde fuera: sin arreglos compartidos de estados abiertos, sin manejadores de clic verificando qué más está expandido. El navegador impone la exclusividad por sí mismo.

<dialog> y la API Popover siguen el mismo patrón arquitectónico a una escala distinta. La plataforma es dueña de open, dueña de la transición entre estados y emite un evento toggle que traslada los mismos valores oldState y newState, ya sea en un panel <details> o en un popover anunciando su propio cambio. Ninguno necesita JavaScript para existir. Todos lo aceptan cuando existe una razón real para que esté allí.

El punto de dolor

Esto se publicó como el FAQ de un centro de ayuda: una lista de preguntas, cada una con un botón y una respuesta oculta, que se abrían y cerraban por completo mediante una alternancia de clases en JavaScript vinculada durante la inicialización de la página. Ningún <details> en ninguna parte. El estado abierto y cerrado existía únicamente como una clase CSS que un escuchador de eventos añadía y quitaba.

La inicialización responsable de vincular esos escuchadores nunca terminó de ejecutarse. La página se renderizó por completo: cada pregunta visible, cada respuesta correctamente oculta, nada faltante a simple vista. Los botones parecían exactamente botones. Presionar uno no hacía nada, porque nadie le había indicado al navegador lo que debía hacer esa presión. La interacción no tenía un respaldo nativo al cual caer, porque no había un mecanismo nativo debajo en primer lugar. El código de la aplicación lo había reemplazado por completo.

Esto no es un argumento de que JavaScript sea poco confiable. El defecto es arquitectónico: la implementación hizo que la inicialización exitosa del script fuera una condición previa para sostener un estado que la plataforma ya podía representar y gestionar de forma nativa.

Las Notas desde el pase del sábado reconstruyen este mismo FAQ sobre <details name>, eliminan cada línea de la lógica original de alternancia y confirman que el comportamiento de acordeón se sostiene sin necesidad de cargar el archivo JavaScript.

La lección más amplia

Esto no es un argumento en contra de que JavaScript sea dueño del estado de interacción. Existe mucho estado que no tiene equivalente nativo y nunca lo tendrá: qué registro está seleccionado, si una solicitud está pendiente, la identidad bajo la cual un usuario está autenticado. Nada de eso pertenece a HTML, y nada de eso debería pertenecer.

La pregunta a la que este artículo vuelve es más acotada. Antes de construir un mecanismo, verifica si la plataforma ya posee uno. El Artículo 4 planteó esto sobre la contención del foco dentro de un modal. El Artículo 5 lo planteó sobre el anclaje de una superposición al elemento correcto. Esta es la misma pregunta dirigida al caso más simple posible: si algo está abierto o cerrado. Y el caso más simple es exactamente donde el hábito de recurrir primero a JavaScript resulta más difícil de notar, porque es tan sencillo de construir que nadie se detiene a verificar si construirlo era necesario.

Lo que falla es una arquitectura que responsabilizó a un tercero —un script, una solicitud de red, un orden de inicialización— de un estado que la plataforma ya era capaz de sostener por sí misma.

Top comments (0)