Fallback de imágenes con evento error en JS

Resumen

Cuando una API devuelve pósters rotos o vacíos, tu interfaz queda con huecos blancos y el atributo alt como única pista. La solución pasa por detectar el evento error de la imagen en JavaScript y reemplazar el src por una imagen de fallback, además de corregir un detalle de CSS que afecta al lazy loading.

Cómo detecto cuándo falla una imagen en JavaScript

El truco está en escuchar el evento error directamente sobre el elemento <img>. Cuando la URL del póster devuelve 404 o la propiedad llega como null desde la API de Movie DB, el navegador dispara ese evento y ahí es donde tú entras a actuar [02:10].

Dentro de la función que crea cada tarjeta de película, agregas un addEventListener sobre movieImage escuchando el evento error. Cuando ese error ocurre, llamas a setAttribute('src', ...) con la URL de tu imagen por defecto. Así el navegador reemplaza el póster roto sin que el usuario vea un espacio vacío.

¿Qué evento detecta cuando una imagen no carga en HTML? El evento error del elemento <img>. Se dispara cuando el recurso devuelve 404, está corrupto o el src es inválido, y permite ejecutar lógica de fallback.

Por qué el orden del setAttribute importa

Un detalle fácil de pasar por alto: el addEventListener del error debe ir después del setAttribute original que asigna el póster real. Si lo pones antes, corres el riesgo de que el orden de ejecución sobrescriba tu lógica de reemplazo o genere comportamientos confusos. Mantén el listener debajo y tu código queda más predecible [04:30].

Cómo agrego una imagen de fallback cuando el póster no existe

La imagen por defecto puede ser cualquier recurso accesible por URL. En la clase se usa una ilustración tipo astronauta de Platzi que dice "Algo falló", pero lo ideal es que crees tu propio asset o uses uno libre de derechos.

El flujo queda así:

  • Asignas el src real de la API con setAttribute.
  • Agregas el addEventListener('error', ...) sobre la misma imagen.
  • Dentro del callback, vuelves a llamar a setAttribute('src', urlDeFallback).

Al recargar y buscar películas como Vengadores o títulos con pocos resultados como Holly, las cartas que antes mostraban el alt sobre fondo blanco ahora muestran tu ilustración de error. La interfaz se siente completa.

Por qué el lazy loading carga todas las imágenes a la vez

Aquí viene lo interesante. Después de arreglar los pósters faltantes, aparece otro síntoma raro: en la sección de películas por categoría, todas las imágenes se cargan al mismo tiempo aunque tengas el lazy loading activo. ¿Cómo así?

El problema no es JavaScript, es CSS. Cuando una imagen no ha cargado, su height por defecto es prácticamente cero, porque el navegador todavía no sabe sus dimensiones. Si todas las tarjetas miden cero de alto, todas caben en el viewport al mismo tiempo, y el IntersectionObserver reporta isIntersecting: true para cada una. Resultado: tu lazy loader las dispara todas en bloque [09:15].

¿Qué es isIntersecting en el IntersectionObserver? Es una propiedad booleana que indica si el elemento observado está dentro del viewport o del root definido. Cuando es true, tu callback se ejecuta y puedes cargar el recurso.

Cómo lo compruebas en consola

Una forma rápida de verificarlo es comentar la línea que asigna el src real dentro del lazy loader y dejar solo un console.log(entry.target) para los elementos con isIntersecting en true. Al recargar, ves en la consola que todas las películas se reportan como intersectando, confirmando la hipótesis del alto cero.

Cómo soluciono el lazy loading con min-height en CSS

La cura es darle a cada .movie-img un min-height que reserve espacio aunque la imagen aún no haya cargado. En la clase se prueban varios valores y se aterriza en 175 píxeles como un mínimo razonable que funciona para distintos tamaños de pantalla.

css .movie-img { min-height: 175px; }

Con ese estilo aplicado, cada contenedor ocupa altura desde el primer render. El navegador ahora sí puede calcular qué tarjetas están dentro del viewport y cuáles no, y el IntersectionObserver reporta isIntersecting: true solo para las primeras seis o cuatro visibles, según el ancho del navegador.

Después de aplicar el min-height, vuelves a activar la línea del setAttribute('src', url) dentro del lazy loader y desactivas el console.log. Al recargar con caché limpio y revisar la pestaña Network filtrando por imágenes, solo cargan las pocas tarjetas visibles, y el resto se va sumando conforme haces scroll.

Qué aprendiste sobre optimización de imágenes en el front-end

Este módulo cierra con dos arreglos que conviven en el mismo flujo:

  • Fallback con evento error: usas addEventListener('error') sobre la imagen y reemplazas el src cuando la API no entrega póster.
  • Min-height para lazy loading: das altura mínima a los contenedores con CSS para que el IntersectionObserver pueda discriminar qué está realmente en pantalla.

No puedes hacer que las imágenes carguen más rápido, pero sí controlar cuándo y cómo se cargan según la interacción del usuario. ¿Tú qué imagen de fallback usarías en tu proyecto? Cuéntalo en los comentarios.