Una simple introducción a la API de Historial en JavaScript
La API de historial es una de esas técnicas de JavaScript que todo desarrollador web necesita conocer en frío. Sin él, un solo clic del botón Atrás saltará directamente de su aplicación. Perderás cualquier trabajo en curso y el usuario se verá obligado a empezar de nuevo.
Afortunadamente, hay una solución fácil. Desde la formalización de HTML5 (y el lanzamiento de Internet Explorer 10), hemos tenido una API de historial que le permite agregar a la lista de historial y reaccionar a la navegación hacia atrás y hacia adelante. En este artículo, aprenderás cómo funciona y lo probarás con un ejemplo en vivo.
Pero antes de seguir adelante, echemos un vistazo más de cerca al problema.
En los viejos tiempos de la web, una página web hacía una cosa: mostrar cosas. Cuando querías ver algo más, hiciste clic en un enlace y fuiste a una nueva URL.
A medida que JavaScript se hizo más poderoso, los desarrolladores se dieron cuenta de que cada página web podía ser una aplicación completa por derecho propio. Una página web podría rivalizar con una aplicación de escritorio! El problema era que los botones Atrás y adelante del navegador no se ajustaban a este nuevo modelo de aplicación. Por ejemplo, un usuario puede realizar una secuencia de tareas en una aplicación web de una sola página y luego esperar usar el botón Atrás para retroceder un solo paso. En su lugar, el botón Atrás regresaría a la página web anterior, cerrando efectivamente la aplicación JavaScript en medio de lo que estuviera haciendo.
a Menudo, este comportamiento no era intuitivo. Por ejemplo, considere una aplicación que use el objeto XMLHttpRequest
para obtener nuevos datos y actualizar la página. Para el usuario, puede parecer que está viajando a una nueva página. Pero si cometen el error de usar el botón de Atrás, la ilusión se rompe y todo se vuelve hacia los lados.
echemos un vistazo más de cerca. Este es un ejemplo que le permite recorrer el Dictionnaire Infernal, un famoso libro (y ahora de dominio público) de 1818 que describe extrañas bestias de la superstición y la mitología. El truco es que los enlaces Anterior y siguiente no te llevan a otra página. En su lugar, activan una llamada asíncrona a través de XMLHttpRequest
que obtiene la información para la siguiente diapositiva. A continuación, el código JavaScript actualiza el cuadro de diapositivas, sin alterar el contenido del resto de la página.
Aquí hay una versión en vivo del ejemplo, completa con compatibilidad con la API de historial. A pesar de que todo tiene lugar en una sola página web, puede usar los botones Adelante y atrás para pasar sin problemas de una imagen a la siguiente. Incluso puedes usar el botón Recargar (recarga la diapositiva actual, en lugar de reiniciar toda la aplicación), y puedes colocar marcadores que mantengan el estado actual, en otras palabras, te llevarán directamente a la diapositiva que estás viendo actualmente.
Y aquí hay una versión de la vieja escuela del ejemplo que no usa la API de historial. No importa lo lejos que estés en el espectáculo lateral: cuando usas el botón Atrás, vuelves a la página web anterior. Los botones de avance y recarga están igualmente rotos. Marcadores reinicie la aplicación desde el principio.
Puedes descargar todo el código fuente aquí para jugar. (Sin embargo, no funcionará localmente, porque utiliza el objeto XMLHttpRequest
para realizar solicitudes web. En el resto de este artículo, explicaré cómo funciona todo.
Preparación del ejemplo
Antes de entrar en la API de Historial, es importante tener una comprensión básica de cómo funciona este ejemplo. Todo es bastante simple.
La página utiliza un número para realizar un seguimiento de la diapositiva que se muestra actualmente. Al hacer clic en Siguiente o Anterior, el código cambia el número de diapositiva actual. Luego utiliza su propia función goToSlide()
para actualizar la página.
La función goToSlide()
es donde se lleva a cabo el trabajo real. Inicia una solicitud asícrona utilizando el objeto infame XMLHttpRequest
.
Depende de usted decidir cómo se genera el contenido de la diapositiva y cómo se envía de vuelta al código de llamada de la página. Por lo general, tendrá algún tipo de marco de enrutamiento que desencadena el código correcto del lado del servidor. Ese código del lado del servidor puede tomar el contenido de la diapositiva de una caché, una base de datos, un archivo o incluso un bloque de marcado codificado.
En este ejemplo, he mantenido las cosas lo más simples posible. La solicitud toma el marcado HTML de un archivo estático. Así que si estás solicitando la tercera diapositiva (con una URL relativa como Diapositivas/3), obtienes un fragmento de marcado HTML solo para esa diapositiva. ¡Tranquilo!
Cuando finaliza la solicitud, el navegador activa el controlador de eventos newSlideReceived()
. Luego es simplemente cuestión de tomar el marcado recibido e insertarlo en la página, utilizando un marcador de posición <div>
.
Eso es todo lo que necesita saber para construir la versión ingenua de esta página, que no admite el historial del navegador.
El objeto de historial
El objeto history
ha existido casi desde los albores de JavaScript. Pero durante gran parte de su vida, fue mucho menos poderoso (y mucho menos útil) de lo que es hoy en día.
El objeto original history
tiene solo una propiedad y tres métodos básicos. La propiedad es length
, y le indica cuántas entradas hay en la lista del historial del navegador:
alert("You have " + history.length + " pages in the history.");
Este detalle es casi inútil.
Las tres originales history
métodos back()
(envía un visitante un paso atrás en el historial de navegación), forward()
(envía un visitante un paso adelante) y go()
(toma un desplazamiento numérico que indica al navegador cómo muchos pasos hacia adelante o hacia atrás). Todo esto suma relativamente poco, a menos que desee diseñar sus propios botones de atrás y adelante personalizados en una página web.
El HistoryAPI añade dos ingredientes mucho más útiles: el método pushState()
y el evento onPopState
.
Agregar una entrada a la lista de historial
El método pushState()
es la pieza central de la API de historial. Le permite agregar un nuevo elemento a la lista de historial de la página actual. Aquí es lo que parece:
En este punto, usted podría preguntarse — ¿cuál es el punto de tener más de una entrada para la misma página? El truco es que cada entrada tiene un estado adjunto, una pieza de información u objeto enlazado que usted establece. Cuando el usuario retrocede a través de la lista de historial, obtiene la información de estado correspondiente para que pueda devolver la página a su versión anterior.
El método pushState()
toma tres argumentos:
- Datos. El primer argumento es cualquier dato que desee almacenar para representar el estado actual de esta página. Usarás esta información para restaurar la página cuando el usuario regrese a la lista de historial.
- Título. El segundo argumento es el título de la página que desea que muestre el navegador. Actualmente, todos los navegadores están unificados para ignorar este detalle. (Por lo que solo puede pasar
null
.) - URL. El tercer argumento es la nueva versión de la URL que desea mostrar para la página en la barra de direcciones del navegador. Esto le permite agregar un mejor soporte para el botón de recarga y los marcadores del navegador.
En el ejemplo de presentación de diapositivas de Dictionnaire Infernal, es fácil agregar soporte de historial Todo lo que necesita para realizar un seguimiento es el número de diapositiva. Se agrega a la lista de historial cada vez que cambia la diapositiva.
Aquí está la línea de código que necesita agregar:
history.pushState(slideNumber, null, null);
Notará que este código no cambia el título de la página con el segundo argumento (porque de todos modos no funciona) y no cambia la URL (verá cómo hacerlo en un momento). Todo lo que hace es almacenar el número de diapositiva.
Para obtener la imagen completa, aquí están las dos llamadas a history.pushState()
en su lugar adecuado: los controladores de eventos para los enlaces Siguiente y anterior:
Volver a una página anterior
Cuando se utiliza la etiqueta pushState()
método, usted también necesita pensar acerca de su contraparte natural, el onPopState
evento. Mientras que el método pushState()
coloca una nueva entrada en la lista de historial del navegador, el evento onPopState
le da la oportunidad de lidiar con él cuando el usuario regrese.
Para entender cómo funciona esto, considere lo que sucede si un visitante recorre todas las diapositivas en el ejemplo Dictionnaire Infernal. A medida que se carga cada diapositiva nueva, la página agrega una entrada de historial. Pero si el usuario retrocede con el botón Atrás, no pasa nada.
Para corregir esta deficiencia, debe manejar el evento onPopState
, que se activa después de cada navegación del historial. (Esto incluye si utiliza uno de los métodos de historial, como history.back()
, así como cuando el usuario hace clic en los botones de navegación del navegador.)
El evento onPopState
proporciona a su código la información de estado que almacenó anteriormente con pushState()
. En este caso, es solo el número de diapositiva, que puede tomar de la propiedad state
del argumento de evento, de la siguiente manera:
var slideNumber = e.State;
Su trabajo es usar la información de estado para restaurar la versión adecuada de la página. En el ejemplo actual, eso significa cargar la diapositiva correspondiente llamando a la práctica función goToSlide()
que maneja toda la navegación de diapositivas. El código completo es corto y sencillo:
Observe que usted necesita para comprobar si e.State
es null
, que sucede en algunos de los navegadores cuando la página se carga por primera vez, y no hay ningún estado guardado.
Cambiar la URL
La versión actual de este ejemplo admite la lista de historial, pero no funciona bien con el botón Recargar. Por ejemplo, si hace clic en la tercera diapositiva y, a continuación, actualiza la página, regresará al principio. Te encontrarás en la misma situación si intentas marcar una de las diapositivas: regresa al marcador y comenzarás de nuevo con la primera diapositiva.
La solución a estos problemas es usar la API de Historial para modificar la URL. Pero hay una razón importante por la que dejé este paso para el final. A menudo, los ejemplos que usan la API de historial cambian la URL, pero no la nueva URL. Esto puede llevar a una aplicación web que se rompe cuando el usuario la actualiza (con un feo error 404), o va a una versión similar pero ligeramente diferente de la página. Si no estás seguro de cómo manejar diferentes URL, no hay vergüenza en usar el enfoque simplificado del ejemplo anterior.
Así que digamos que estás listo para sumergirte, crear mejores URL y jugar bien con los marcadores y las actualizaciones del navegador. Hay dos enfoques básicos que puede tomar:
- Cambiar el nombre de la página URL por completo. En el ejemplo Dictionnaire Infernal, puede cambiar la URL a Slide1.html, Deslizador 2.html, y así sucesivamente a medida que el usuario se mueve a través de las diapositivas. El problema es que necesita tener páginas reales con estos nombres (no haga eso, es engorroso), o necesita configurar el enrutamiento para que cuando su servidor web reciba una solicitud de Slide2.html ejecuta código que crea la versión correcta de la página.
- Mantenga el mismo nombre de página pero agregue información a la cadena de consulta. Este es un enfoque más simple y de menor escala. A medida que el usuario viaja a través de la presentación de diapositivas, terminarás con URL como Presentación de diapositivas.html?diapositiva=1, luego presentación de diapositivas.html?diapositiva = 2, presentación de diapositivas.html?diapositiva=3, y así sucesivamente. El beneficio de este enfoque es que es trivialmente fácil escribir un pequeño fragmento de JavaScript que comprueba la cadena de consulta y se asegura de que esté en la diapositiva correcta cuando se vuelve a cargar la página. No necesita ningún código de servidor para que esto funcione, una página puede hacerlo todo.
Aquí hay una versión modificada de la llamada pushState()
que vio anteriormente y que utiliza el segundo enfoque. Este código introduce el número de diapositiva al final de la URL:
history.pushState(slide, null, "Dictionnaire.html?slide=" + slide);
Y aquí está el código que se ejecuta cuando se recarga la página. Comprueba la información de la diapositiva en la URL y, si encuentra un número adecuado, llama a la función goToSlide()
para cargar la diapositiva.
Ahora usted nunca perderá su lugar en la presentación de diapositivas. Puedes probar la versión completa de este ejemplo o descargarlo todo aquí.
Detalles finales
La API de historial es fácil de usar, pero también facilita la creación de problemas más grandes que se resuelven. Ahora que ya sabes cómo encaja todo, aquí tienes algunos consejos finales:
- Si presionas, debes hacer pop. El método
pushState()
y el eventoonPopState
son socios. Lo que pones con uno, lo vuelves con el otro. - No cambie la URL a menos que esté listo para manejarla. Es bueno cambiar tu URL para reflejar el cambio de página, pero debes ser capaz de manejar las solicitudes de cada URL que uses. Si tu URL apunta a una página que no existe, la aplicación se interrumpirá en las actualizaciones y los marcadores.
- Utilice un objeto de estado personalizado para almacenar más información. No necesitas quedarte con un número simple. Puede poner un objeto personalizado completo en estado, que puede agrupar toda la información que necesita para una tarea compleja.