
La API de historial de HTML5 ofrece a los desarrolladores la posibilidad de modificar la URL de un sitio web sin actualizar la página completa. Esto es particularmente útil para cargar partes de una página con JavaScript, de modo que el contenido sea significativamente diferente y justifique una nueva URL.
He aquí un ejemplo. Digamos que una persona navega desde la página de inicio de un sitio a la página de Ayuda. Estamos cargando el contenido de esa página de ayuda con Ajax. Luego, ese usuario se dirige a la página Productos, que nuevamente cargamos e intercambiamos contenido con Ajax. Entonces quieren compartir la URL. Con la API de historial, podríamos haber estado cambiando la URL de la página junto con el usuario mientras navega, de modo que la URL que ven (y, por lo tanto, comparten o guardan) sea relevante y correcta.
Los basicos
Para ver las características de esta API, es tan simple como dirigirse a las Herramientas para desarrolladores y escribir history
en la consola. Si la API es compatible con el navegador de su elección, encontraremos una gran cantidad de métodos adjuntos a este objeto:
Estos son los métodos que tenemos disponibles para manipular el historial del navegador.
Estamos interesados en el pushState
y replaceState
métodos en este tutorial. Volviendo a la consola, podemos experimentar un poco con los métodos y ver qué pasa con la URL cuando los usamos. Cubriremos los otros parámetros en esta función más adelante, pero por ahora todo lo que necesitamos usar es el parámetro final:
history.replaceState(null, null, 'hello');
El replaceState
El método anterior cambia la URL en la barra de direcciones con ‘/ hola’ a pesar de que no se solicitan activos y la ventana permanece en la misma página. Sin embargo, aquí hay un problema. Al presionar el botón Atrás, veremos que no regresamos a la URL de este artículo, sino que regresaremos a cualquier página en la que estuviéramos antes. Esto es porque replaceState
no manipula el historial del navegador, simplemente reemplaza la URL actual en la barra de direcciones.
Para solucionar esto, necesitaremos usar el pushState
método en su lugar:
history.pushState(null, null, 'hello');
Ahora, si hacemos clic en el botón Atrás, deberíamos encontrarlo funcionando como nos gustaría, ya que pushState
ha cambiado nuestro historial para incluir cualquier URL que acabamos de pasar. Esto es interesante, pero ¿qué sucede si intentamos algo un poco tortuoso y pretendemos que la URL actual no era css-tricks.com en absoluto, sino otro sitio web por completo?
history.pushState(null, null, 'https://twitter.com/hello');
Esto arrojará una excepción porque la URL debe ser del tipo mismo origen como el actual, de lo contrario, podríamos arriesgarnos a fallas de seguridad importantes y brindar a los desarrolladores la capacidad de engañar a las personas haciéndoles creer que están en un sitio web completamente diferente.
Volviendo a esos otros parámetros que se pasan a este método, podemos resumirlos así:
history.pushState([data], Using the HTML5 History API | CSS-Tricks, [url]);
- El primer parámetro son los datos que necesitaremos si cambia el estado de la página web, por ejemplo, cada vez que alguien presiona el botón de avance o retroceso en su navegador. Tenga en cuenta que en Firefox estos datos están limitados a 640k caracteres.
title
es el segundo parámetro que puede ser una cadena, pero en el momento de escribir este artículo, todos los navegadores simplemente lo ignoran.- Este último parámetro es la URL que queremos que aparezca en la barra de direcciones.
Una historia rápida
Lo más significativo de estas API de historial es que no recargan la página. En el pasado, la única forma de cambiar la URL era cambiar la window.location
que siempre recarga la página. Excepto, si todo lo que cambiaste fue el hash
(como cómo hacer clic en un <a href="https://css-tricks.com/using-the-html5-history-api/#target">link</a>
no recarga la página).
Esto llevó al antiguo método hashbang de cambiar la URL sin actualizar la página completa. Twitter solía hacer las cosas de esta manera y fue muy criticado por ello (un hash no es una ubicación de recursos “real”).
Twitter se alejó de eso y fue uno de los primeros defensores de esta API. En 2012 el equipo describió su nuevo enfoque. Aquí describen algunos de sus problemas al trabajar en este tipo de escala y también detallan cómo varios navegadores implementan esta especificación.
Un ejemplo usando pushState y Ajax
¡Construyamos una demostración!
En nuestra interfaz imaginaria queremos que los usuarios de nuestro sitio web encuentren información sobre un personaje de Ghostbusters. Cuando seleccionan una imagen, necesitamos que el texto sobre ese personaje aparezca debajo y también queremos agregar una clase actual a cada imagen para que quede claro quién ha sido seleccionado. Luego, cuando hagamos clic en el botón de retroceso, la clase actual saltará al carácter previamente seleccionado (y viceversa para el botón de avance) y, por supuesto, necesitaremos el contenido de abajo para volver a cambiar también.
Aquí hay un ejemplo práctico que podemos analizar:
El marcado de este ejemplo es bastante simple: tenemos un .gallery
que contiene algunos enlaces y dentro de cada uno de ellos hay una imagen. Luego tenemos el texto debajo que queremos actualizar con el nombre seleccionado y el vacío .content
div que queremos reemplazar con los datos de los respectivos archivos HTML de cada personaje:
<div class="gallery">
<a href="https://css-tricks.com/peter.html">
<img src="bill.png" alt="Peter" class="peter" data-name="peter">
</a>
<a href="http://css-tricks.com/ray.html">
<img src="ray.png" alt="Ray" class="ray" data-name="ray">
</a>
<a href="http://css-tricks.com/egon.html">
<img src="egon.png" alt="Egon" class="egon" data-name="egon">
</a>
<a href="http://css-tricks.com/winston.html">
<img src="winston.png" alt="Winston" class="winston" data-name="winston">
</a>
</div>
<p class="selected">Ghostbusters</p>
<p class="highlight"></p>
<div class="content"></div>
Sin JavaScript, esta página seguirá funcionando como debería, al hacer clic en un enlace se dirige a la página de la derecha y hacer clic en el botón Atrás también funciona como lo esperaría un usuario. ¡Hurra por la accesibilidad y la elegante degradación!
A continuación, saltaremos a JavaScript, donde podemos comenzar a agregar un controlador de eventos a cada enlace dentro del .gallery
elemento mediante el uso de la propagación de eventos, así:
var container = document.querySelector('.gallery');
container.addEventListener('click', function(e) {
if (e.target != e.currentTarget) {
e.preventDefault();
// e.target is the image inside the link we just clicked.
}
e.stopPropagation();
}, false);
Dentro de esto if
declaración podemos entonces asignar la data-name
atributo de la imagen que seleccionamos al data
variable. Luego, le agregaremos “.html” y lo usaremos como tercer parámetro, la URL que nos gustaría cargar, en nuestro pushState
método (aunque en un ejemplo real probablemente querríamos cambiar la URL solo después de que la solicitud Ajax haya sido exitosa):
var data = e.target.getAttribute('data-name'),
url = data + ".html";
history.pushState(null, null, url);
// here we can fix the current classes
// and update text with the data variable
// and make an Ajax request for the .content element
// finally we can manually update the document’s title
(Alternativamente, también podríamos tomar el atributo href del enlace para esto).
Reemplacé el código de trabajo con comentarios para que podamos enfocarnos en el pushState
método por ahora.
Entonces, en este punto, hacer clic en una imagen actualizará la barra de URL y el contenido con la solicitud Ajax, pero hacer clic en el botón Atrás no nos enviará al carácter anterior que seleccionamos. Lo que debemos hacer aquí es realizar otra solicitud Ajax cuando el usuario hace clic en el botón Atrás / Adelante y luego necesitaremos actualizar la URL una vez más con pushState
.
Primero regresaremos y actualizaremos el parámetro de estado de nuestro pushState
método para esconder esa información:
history.pushState(data, null, url);
Este es el primer parámetro, data
en el método anterior. Ahora, cualquier cosa que esté configurada para esa variable será accesible para nosotros en un popstate
evento que se activa cada vez que el usuario hace clic en los botones de avance o retroceso.
window.addEventListener('popstate', function(e) {
// e.state is equal to the data-attribute of the last image we clicked
});
En consecuencia, podemos usar esta información como queramos, que en este caso es pasar el nombre del Ghostbuster anterior que seleccionamos como parámetro al Ajax. requestContent
función, que usa jQuery’s load
método:
function requestContent(file) {
$('.content').load(file + ' .content');
}
window.addEventListener('popstate', function(e) {
var character = e.state;
if (character == null) {
removeCurrentClass();
textWrapper.innerHTML = " ";
content.innerHTML = " ";
document.title = defaultTitle;
} else {
updateText(character);
requestContent(character + ".html");
addCurrentClass(character);
document.title = "Ghostbuster | " + character;
}
});
Si un usuario hiciera clic en la imagen de Ray, nuestro oyente de eventos se dispararía, que luego almacenaría el atributo de datos de nuestra imagen dentro del pushState
evento. En consecuencia, esto carga el ray.html
archivo al que se llamará si el usuario selecciona otra imagen y luego hace clic en el botón Atrás. *Uf*.
¿Qué nos deja esto? Bueno, si hacemos clic en un carácter y luego compartimos la URL que hemos actualizado, entonces ese archivo HTML se cargaría en su lugar. Puede ser una experiencia menos confusa y preservaremos la integridad de nuestras URL mientras brindamos a nuestros usuarios una experiencia de navegación más rápida en general.
Es importante reconocer que el ejemplo anterior es simplista, ya que cargar contenido de esta manera con jQuery es muy complicado y probablemente queramos pasar un objeto más complejo a nuestro pushState
pero nos muestra cómo podemos comenzar a aprender inmediatamente a usar la API de historial. Primero caminamos, luego corremos.
El siguiente paso
Si tuviéramos que utilizar esta técnica a mayor escala, probablemente deberíamos considerar el uso de una herramienta diseñada específicamente para ese propósito. Por ejemplo, pjax es un complemento de jQuery que acelera el proceso de usar Ajax y pushState simultáneamente, aunque solo admite navegadores que usan la API de historial.
History JS, por otro lado, es compatible con navegadores más antiguos con el antiguo hash-fallback en las URL.
URL geniales
Me gusta pensar en las URL, y en particular hago referencia a esta publicación sobre el diseño de URL de Kyle Neath todo el tiempo:
Las URL son universales. Funcionan en Firefox, Chrome, Safari, Internet Explorer, cURL, wget, tu iPhone, Android e incluso se escriben en notas adhesivas. Son la única sintaxis universal de la web. No dé eso por sentado. Cualquier usuario semi-técnico habitual de su sitio debería poder navegar el 90% de su aplicación basándose en la memoria de la estructura de la URL. Para lograr esto, sus URL deberán ser pragmáticas.
Esto significa que, independientemente de los trucos o trucos para mejorar el rendimiento que deseemos implementar, los desarrolladores web deben apreciar la URL y, con la ayuda de la API de historial de HTML5, podemos solucionar problemas como el ejemplo anterior con solo un poco de esfuerzo.
Problemas comunes
- A menudo es una buena idea incrustar la ubicación de una solicitud Ajax en el
href
atributos de un elemento de anclaje. - Asegurate que
return true
de los controladores de clic de Javascript cuando las personas hacen clic en el medio o en un comando para que no los anulemos accidentalmente.
Otras lecturas
- Documentación de Mozilla sobre la manipulación del historial del navegador
- El ejemplo de la galería Ajax de Dive into HTML5
- Implementación de Twitter de pushState
Soporte del navegador
Cromo | Safari | Firefox | Ópera | ES DECIR | Androide | iOS |
---|---|---|---|---|---|---|
31+ | 7.1+ | 34+ | 11,50+ | 10+ | 4.3+ | 7.1+ |