Maquetación Web Dinámica / JavaScript para maquetadores: darle vida al DOM
IntersectionObserver: reaccionar al scroll
Revelar elementos cuando entran en pantalla, cargar imágenes solo cuando se
ven, cambiar el navbar al pasar el hero: todo eso necesita saber "¿este
elemento está visible?". La forma ingenua —escuchar el evento scroll y
medir posiciones— es lenta y torpe. IntersectionObserver lo hace por ti,
eficiente y en una API elegante. Es, con razón, apodado el mejor amigo del
maquetador.
El vigía que te avisa
El patrón: observar y reaccionar
// crear el vigía: una función que corre cuando cambia la visibilidad
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) { // ¿entró en pantalla?
entry.target.classList.add("visible"); // revélalo
observer.unobserve(entry.target); // ya está: deja de vigilarlo
}
});
}, { threshold: 0.2 }); // dispara cuando el 20% del elemento es visible
// ponerle el vigía a cada elemento que quieras observar
document.querySelectorAll(".revelar").forEach((el) => observer.observe(el));Tres piezas: creas el observer con una función que recibe las entries
(los elementos que cambiaron de visibilidad); revisas entry.isIntersecting
(¿está visible?); y observe() cada elemento. El unobserve tras revelar
es una optimización clave: una vez hecho el efecto, dejas de vigilar. El
threshold decide cuánto debe verse para disparar (0.2 = 20%).
Reveal al scroll (con empatía)
<div class="scroll">
<p class="intro">Desplázate hacia abajo ↓</p>
<section class="revelar"><h3>Bloque 1</h3><p>Aparezco al entrar en pantalla.</p></section>
<section class="revelar"><h3>Bloque 2</h3><p>Yo también, con un desfase natural.</p></section>
<section class="revelar"><h3>Bloque 3</h3><p>El vigía me avisó justo a tiempo.</p></section>
<section class="revelar"><h3>Bloque 4</h3><p>Sin escuchar el evento scroll ni una vez.</p></section>
</div>body { font-family: system-ui; margin: 0; background: #f8fafc; }
.scroll { height: 340px; overflow-y: auto; padding: 16px; }
.intro { text-align: center; color: #64748b; font-size: 14px; margin: 8px 0 200px; }
.revelar { background: white; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; margin-bottom: 180px; opacity: 0; transform: translateY(24px); transition: opacity 500ms ease-out, transform 500ms ease-out; }
.revelar.visible { opacity: 1; transform: translateY(0); }
.revelar h3 { margin: 0 0 6px; color: #6d28d9; }
.revelar p { margin: 0; color: #475569; font-size: 14px; }
@media (prefers-reduced-motion: reduce) { .revelar { transition: none; } }const items = document.querySelectorAll(".revelar");
const quieto = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (quieto) {
// sin animación: muéstralo todo de una (nada oculto para quien redujo movimiento)
items.forEach((el) => el.classList.add("visible"));
} else {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("visible");
observer.unobserve(entry.target);
}
});
}, { threshold: 0.25 });
items.forEach((el) => observer.observe(el));
}Desplázate y mira los bloques aparecer al entrar. Nota el mismo cuidado de
siempre: si el usuario pidió prefers-reduced-motion, saltamos el observer
y mostramos todo de inmediato — nada de contenido atrapado en opacity: 0
para quien no verá la animación. La lección de empatía del módulo 11, ahora
en JavaScript.
Sus otros dos superpoderes
// LAZY LOADING: cargar la imagen solo cuando está por verse
const imgObserver = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
e.target.src = e.target.dataset.src; // recién ahora descarga
imgObserver.unobserve(e.target);
}
});
});
// NAVBAR que cambia: observa un "centinela" invisible arriba del todo
const navObserver = new IntersectionObserver(([entry]) => {
navbar.classList.toggle("compacto", !entry.isIntersecting); // fuera del hero → compacto
});Para reveals SIMPLES, recuerda que el módulo 11 tiene la versión 100% CSS
(animation-timeline: view()). ¿Cuándo cada uno? CSS puro si el efecto es
decorativo y el soporte alcanza; IntersectionObserver cuando necesitas
LÓGICA (cargar datos, disparar un contador, cambiar el navbar) o soporte
más amplio. Saber elegir es el criterio que este curso te viene entrenando.
Para un efecto de 'aparecer al hacer scroll' en 50 tarjetas, ¿por qué IntersectionObserver es mejor que escuchar el evento scroll y medir posiciones?
Mini-reto
Dale a "El Observador" dos mejoras con un mismo observer o dos: (1) las
tarjetas de noticia aparecen con un fade-up al entrar en pantalla
(respetando reduced-motion), y (2) el header se vuelve .compacto (más
bajo, con sombra) cuando dejas atrás el título principal —observa un
centinela invisible arriba. Dos patrones que verás en el 90% de las
landings profesionales.
Qué sigue
Tienes todas las piezas: seleccionar, clases, eventos, delegación, render y scroll. La próxima lección las combina en los PATRONES DE UI clásicos que te van a pedir una y otra vez: modal accesible, tabs, acordeón y dark mode persistente. Tu recetario de interactividad.