Líneas de transición entre el estado de CSS y los eventos de JavaScript
CSS nos está escuchando. No, no me gusta Eso. En cambio, CSS está acumulando más y más pseudocategorías para ayudarnos a responder a los eventos de JavaScript para que no tengamos que hacerlo con el propio JavaScript. Sin embargo, aunque las pseudoclases rastrean el estado en lugar de los eventos, a veces se sienten como detectores de eventos (lo cual no importa en un contexto CSS).
Entonces otra vez, ¿qué? Sí CSS ahora? Por ejemplo, hay un propuesta event-trigger En la especificación de activación de animación, básicamente escucha eventos y activa animaciones. Si me preguntas, la gramática es mucho más que eso (piensa: comando de llamada Pero para CSS).
Pero para adaptarme a la realidad actual, lo guiaré a través de las diferentes pseudoclases de CSS, que son como detectores de eventos, y luego event-triggerTe mostraré cómo (creo) funciona esta característica actualmente no compatible.
Pseudoclase “Event Listener”
:hover y :active
este :hover Captura de estado desde cuando pointerenter Cuando se desencadena el evento pointerleave Los eventos se activan, lo que ilustra perfectamente por qué las pseudoclases son estados, no eventos.
:active Coincide con el objetivo presionado actualmente con el mouse, el dedo o el lápiz (como un enlace o botón), lo que lo convierte en algo así como pointerdown y pointerup/pointercancel.
Por cierto, pointer-events: none ¡La declaración CSS evita que los eventos del indicador se activen en elementos seleccionados!
:focus y :focus-visible
este :focus Pseudoclase similar a focus y blur (enfoque perdido) evento de JavaScript, pero :focus-visible Es un poco complicado. :focus-visible cuando se activa :focus Esto es cierto, pero además, los navegadores utilizan varias heurísticas para determinar si se debe mostrar el indicador de enfoque. ¿El usuario utiliza un teclado? ¿Es este elemento un control de formulario? Realmente me hizo apreciar lo que CSS tiene para ofrecer. De hecho, la mejor manera de manejar este problema con JavaScript es consultar pseudoclases de CSS:
element.addEventListener("focus", (event) => {
if (event.target.matches(":focus-visible")) {
/* Do something */
}
});
:focus-within (y :has())
JavaScript es bueno en cosas como “si A es Y, entonces haz Z con B”. Podemos atravesar el DOM, utilizar la propagación de eventos y más. CSS puede parecer un poco limitado a este respecto. Sin embargo, CSS está evolucionando rápidamente. Tiene muchas funciones nuevas de si esto hace aquello, como animación impulsada por desplazamientoy habrá más en el futuro. HTML utiliza elementos especializados para hacer lo mismo, p. <details>todos tienen funcionalidad CSS incluida.
De hecho, mencionaré algunos de ellos más adelante. En un sentido más amplio, lo que tenemos es :focus-withincoincide si el niño tiene concentración, y :has()que acepta cualquier selector válido y coincide si existe dicha relación entre dos selectores.
Por ejemplo, estos dos selectores realizan exactamente la misma acción:
form:focus-within {
/* Style the form when something within has focus */
}
form:has(:focus) {
/* Style the form when something within has focus */
}
:checked
es bastante obvio :checked Hacer. El evento de JavaScript más similar es changese activará cuando el valor de a alcance <input>, <select>o <textarea> cambios (aunque en este caso, input los acontecimientos son muy similares).
Para escuchar los controles haríamos esto:
checkbox.addEventListener("change", (event) => {
if (event.target.checked) {
/* Checked */
} else {
/* Not checked */
}
});
Las pseudoclases de CSS normalmente capturan el momento entre dos eventos de JavaScript (por ejemplo, pointerenter y pointerleave), pero cuando no lo hacen, se ocupan de la lógica, como se mencionó anteriormente.
Veamos algunos ejemplos más de procesamiento de lógica oculta.
:valid/:invalid/:user-valid/:user-invalid/:autofill
no necesitamos :not() Función de pseudoclase aquí, ya que la validez se puede verificar usando :valid y :invalid Pseudoclases, pero en términos de JavaScript, no hay valid eventos (solo invalid). Dicho esto, si usa JavaScript es posible que deba llamar checkValidity() método (que en realidad desencadena invalid Si se produce una devolución de datos, se produce un evento. false) en la devolución de llamada del oyente del evento input, change, blur (verifique la validez cuando se pierda el foco del elemento), o submit (Compruebe la validez del formulario completo al enviarlo, a continuación).
form.addEventListener("submit", () => {
if (form.checkValidity()) {
/* All form controls are valid */
} else {
/* A form control is invalid (the invalid event fires) */
}
});
También podemos usar ValidityState objeto, no se activará invalid evento, pero nos dice por qué un control de formulario es válido o no válido de la misma manera que la validación de formulario HTML:
input.addEventListener("input", () => {
if (input.validity.valid) {
/* Input is valid */
} else {
/* Input is invalid (the invalid event doesn’t fire) */
}
});
Lo que pasa con la validación de formularios HTML es que se encarga de todo el front-end, pero si necesita un comportamiento no predeterminado, checkValidity() o ValidityState Esto es lo que estás buscando.
Las pseudoclases pueden funcionar de cualquier manera. De hecho, ¡demasiado bueno! Una cosa que se pasa por alto fácilmente es que los controles de formulario activan :valid o :invalid Inmediatamente. Sin embargo, :user-valid y :user-invalid Espera a que el usuario proporcione un valor y elimina el foco antes de activarse. esto es en realidad change El evento lo hace (a menos que el elemento sea una casilla de verificación, un botón de opción, una lista desplegable, un selector de color o un control deslizante de rango) y está relacionado con input evento.
No existe ningún evento de JavaScript para autocompletar, ni siquiera una forma limpia de detectarlo usando JavaScript, pero sí Sí uno :autofill Pseudoclase.
La pseudoclase del elemento multimedia aún es nueva. Chrome aún no los admite y hace poco llegaron a Firefox, pero ya están disponibles. Interoperabilidad 2026 Pronto estaremos estilizando <audio> y <video> El elemento no escucha eventos de JavaScript según su estado. Estoy seguro de que ya entiendes cómo funciona esto, así que aquí tienes una descripción general rápida:
| pseudoclase | Equivalentes de eventos de JavaScript |
|---|---|
:buffering |
waiting |
:muted |
volumechange (pero ver más abajo) |
:paused |
pause |
:playing |
playing (No play) |
:seeking |
seeking |
:stalled |
stalled |
:volume-locked |
No aplicable, ver más abajo |
usar volumechange Detectar eventos silenciosos:
audio.addEventListener("volumechange", () => {
if (audio.muted) {
// Muted
} else {
// Not muted
}
});
Detectar el bloqueo de volumen significa intentar cambiar el volumen y comprobar si tiene éxito. La mejor manera es crear un elemento completamente nuevo para que no activemos volumechange Sobre lo real:
// Create video
const video = document.createElement("video");
// Change volume
video.volume = 0.5;
if (video.volume !== 0.5) {
// Volume locked
} else {
// Volume not locked
}
(o usar :volume-locked Pseudoclase, si escribe CSS. )
:popover-open / :open / :modal
Como era de esperar, no hay eventos de JavaScript cuando aparece la ventana. <dialog>o <details> Encendido o apagado, pero podemos escuchar toggle Luego, el evento verifica el estado:
element.addEventListener("toggle", () => {
if (element.open) {
/* Popover/dialog/details open */
} else {
/* Popover/dialog/details not open */
}
});
Sin embargo, CSS proporciona estas pseudoclases directamente:
:popover-open(para ventanas emergentes):open(para<dialog>y<details>elemento):modal(para modales<dialog>s y elementos de pantalla completa)
Hablando de elementos de pantalla completa….
:fullscreen
este :fullscreen Las pseudoclases son sinónimos. fullscreenchange Eventos de JavaScript condicionales:
document.addEventListener("fullscreenchange", () => {
if (document.fullscreenElement) {
/* fullscreenElement is fullscreen */
} else {
/* Nothing is fullscreen (fullscreenElement is null) */
}
});
:target
Cuando el hash de URL (por ejemplo, #contact) coincide con el ID del elemento (por ejemplo, <div id="contact">), el elemento coincide :target Pseudoclase. Al usar JavaScript, debemos escuchar hashchange evento y luego ver si se encuentra un elemento coincidente:
window.addEventListener("hashchange", () => {
const target = document.getElementById(window.location.hash.substring(1));
if (target) {
/* Matching element found */
} else {
/* Matching element not found */
}
});
Conclusión (pero no realmente)
Esto no es una perorata de “JavaScript es malo”, sino una apreciación por la simplicidad de CSS sin olvidar el control quirúrgico que proporciona JavaScript. Más formas de hacer las cosas nunca son malas.
En este sentido me gustaría mencionar rápidamente event-trigger.
Oyente de eventos real (event-trigger)
me encontré desencadenante de evento Si bien Chrome implementa la animación del activador de desplazamiento, dado que están en el mismo mod, aún no es compatible con ningún navegador web, así que me disculpo si cometí algún error. Echemos un vistazo más de cerca.
event-trigger-name Aceptará un logotipo discontinuo simple:
button {
event-trigger-name: --event;
}
event-trigger-source Básicamente será un oyente de eventos.
Aceptará las siguientes palabras clave:
activateinterestclicktouchdblclickkeypress(<string>)
button {
event-trigger-source: click;
}
Yo creo interest Las palabras clave se refieren a próximos API de llamada de interés y activate Las palabras clave pueden depender del elemento. para <details> Por ejemplo, la activación podría significar cuando está abiertopero no estoy seguro. Los borradores posteriores de la especificación deberían decirnos más y revelar más eventos.
De todos modos, el evento desencadena la animación. Primero creamos un @keyframes animación, luego la adjuntamos al elemento que queremos animar, pero la animación no se ejecuta hasta que se activa el evento (mientras que normalmente se ejecutan inmediatamente).
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
div {
animation: fade-in 300ms both;
}
Luego nos aseguramos de que cuando se active el evento, se active la animación. Hacemos esto a través de la configuración. animation-trigger al lado de animationhaciendo referencia a la marca de la línea de puntos (--event). Un beneficio opcional de hacer esto es permitir que los eventos de un elemento activen animaciones en otro elemento. A continuación se muestra un ejemplo sencillo utilizando event-trigger La abreviatura de este tiempo:
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
button {
/* On click, trigger --event animation */
event-trigger: --event click;
}
div {
/* When --event fires, play animation forwards */
animation-trigger: --event play-forwards;
/* Animation */
animation: fade-in 300ms both;
}
esto es lo que se llama un paísmenos Activador de evento. Piénsalo: no puedes deshacer clic, ¿verdad? Pero podemos perder interés, entonces ese es un estadoLleno La animación activada por el evento se ve así (tenga en cuenta la sintaxis dos Los eventos están separados por un / y dos Acciones animadas, una para cada estado):
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
button {
/* interest (entry) / interest (exit) */
event-trigger: --event interest / interest;
}
div {
/* Play forward with interest, backward when losing it */
animation-trigger: --event play-forwards play-backwards;
/* Animation */
animation: fade-in 300ms both;
}
Las acciones de animación aceptables incluyen:
noneplayplay-onceplay-forwardsplay-backwardspauseresetreplay
Hay muchas combinaciones de eventos y acciones de animación que no funcionarán, pero son fáciles de evitar porque no tiene sentido usarlas. Sin embargo, podemos activar múltiples animaciones diferentes porque animation-trigger es una subpropiedad que solo se puede restablecer animation. He aquí un ejemplo aproximado:
animation-name: animationA, animationB;
animation-trigger: --eventA play, --eventB replay;
Las posibilidades son infinitas, dependiendo de cómo avance el W3C con esta característica (¡la especificación menciona permitir que los eventos surjan!), pero me gustaría que pudiéramos usar activadores de eventos para llamar a métodos JavaScript, tal como lo hace HTML con la API de Invoker Commands.
¿Qué opinas? ¿Es este un paso en la dirección correcta o CSS necesita seguir su propio camino?