History API

Una de las mejoras que acompaña a HTML 5 nos permitirá trabajar de manera más cómoda con peticiones asíncronas y, yendo más allá, con las ya conocidas Single Page Applications, donde nuestro sitio web nos aporta la gran experiencia de usuario, al no sentir la necesidad de generar peticiones completas para tener la información que el usuario necesita en cada momento.
Tiempo atrás, uno de los quebraderos de cabeza para los desarrolladores era encontrar la estrategia más óptima para poder modificar la URL que se mostraba en el navegador, con el objetivo de poder seguir la pista de dónde nos encontrábamos y cómo volver a ese punto tanto a nivel de página cómo mostrar la información pertinente. Hasta ahora se solía hacer uso de hashbangs (#!), aunque ello desembocara en cierta penalización en el rendimiento (Twitter se deshizo de este mecanismo hace relativamente poco tiempo).

History API en versiones anteriores tenía como único cometido el poder manipular el histórico de la sesión actual vía script. Con HTML 5, disponemos de una ampliación considerablemente útil de esta API:

  • history.back(): Genera el mismo resultado que el uso del botón Atrás (Back) del navegador, permitiendo visualizar la página anteriormente visualizada.
  • history.forward(): Nos permite avanzar en el histórico de la ventana, siempre y cuando hayamos retrocedido en el mismo 😉
  • history.length: A medida que vamos visitando páginas el histórico irá creciendo. La forma de conocer el número de páginas a las que podemos regresar es a través de esta propiedad dentro de history.
  • history.go(): Realmente útil ya que nos permite dirigirnos directamente a cualquiera de las páginas almacenadas en el histórico. La forma de indicar a la que nos queremos dirigir es indicando el número relativo desde la posición en la que nos encontramos. (La página actual tiene el valor 0, por lo que usaremos números negativos para retroceder y positivos para avanzar).
  • history.pushState(object,title,url): La parte más innovadora que nos ofrece HTML 5 es la posibilidad de añadir de forma manual entradas al histórico y,al mismo tiempo, actualizar la URL actual (No más hashbangs!).
  • history.replaceState(object,title,url): En este caso, a diferencia del anterior, nos permite modificar una entrada, en lugar de añadirla (por lo que el tamaño del histórico no se verá alterado).
  • window.onpopstate: Este evento a nivel de window trabaja en conjunción con los métodos anteriores. Este evento se lanzará cada vez que avancemos o retrocedamos en el histórico y con ello se refresque la información actual de history.
  • history.state: contiene el objeto inyectado a través de pushState o replaceState. Cada página del histórico tendrá su propio objeto, lo cual nos permitirá almacenar los valores necesarios para que la página a la que volvamos sea totalmente funcional, haciendo peticiones AJAX por ejemplo.

Para ver todas estas características en conjunto, he creado un pequeño ejemplo:

<!DOCTYPE html>
<html>
<head>
    <title>History API</title>
    <style>
        body {
            font-family: 'Segoe UI';
        }
        article {
            width: 50%;
            margin: auto;
        }
        ul {
            list-style: none;
        }
    </style>
</head>
<body>
    <article>
        <header>
            <h1>History API</h1>
        </header>
        <ul>
            <li>
                <a href="/geek">Geek</a>
            </li>
            <li>
                <a href="/nerd">Nerd</a>
            </li>
            <li>
                <a href="/freak">Freak</a>
            </li>
        </ul>
        <output id="output"></output>
        <button id="btnBack">Back</button>
        <button id="btnForward">Forward</button>
    </article>
    <script>
        function showData() {
            var output = document.getElementById("output");
            if (history.state)
                output.innerHTML = "<ul><li><strong>Title</strong>: " + history.state.title +
                                   "</li><li><strong>Description</strong>: " + history.state.description + "</li>" +
                                   "<li><strong>URL</strong>: " + window.location + "</li>" +
                                   "<li><strong>History length</strong>: " + history.length + "</li>";
        }
        (function () {
            var data = {
                "geek": {
                    title: "Definición de Geek",
                    description: "Geek (del inglés geek, pronunciado 'guik': IPA /gi:k/) es un término que se utiliza para referirse a la persona fascinada por la tecnología y la informática."
                },
                "nerd": {
                    title: "Definición de Nerd",
                    description: "Nerd o nerdo es un planteamiento que designa a un estereotipo de persona abocada completamente al estudio y la labor científica, informática e intelectual hasta el punto de mostrar desinterés por las actividades deportivas, físicas ó sociales. ..."
                },
                "freak": {
                    title: "Definición de Freak",
                    description: "Persona obsesionada con un determinado hobby hasta el punto de separarse de todo lo que se suele considerar normal y variar su propio aspecto. Hablamos de, por ejemplo, los góticos. No de Carmen de Mairena."
                }
            };
            [].forEach.call(document.querySelectorAll("a"), function (item) {
                item.addEventListener("click", function (e) {
                    e.preventDefault();
                    var title = item.innerText;
                    history.pushState(data[title.toLowerCase()], title, item.getAttribute("href"));
                    showData();
                });
            });
            var btnBack = document.getElementById("btnBack"),
            btnForward = document.getElementById("btnForward");
            btnBack.addEventListener("click", function () {
                history.back();
            });
            btnForward.addEventListener("click", function () {
                history.forward();
            });
            window.onpopstate = function () {
                showData();
            };
        })();
    </script>
</body>
</html>

Como curiosidad, si utilizáis JSFiddle o Codepen.io se podrá visualizar el state del histórico pero podréis comprobar que la URL no varía. Esto es debido a que estas herramientas online utilizan frames para los distintos apartados y no aplica directamente a la ventana principal 🙂

Espero que haya sido de utilidad.

¡Saludos!