Incluídas na listagem de novas características que estão aparecendo no mundo de desenvolvimento da web com HTML5, há duas muito interessantes: Gerenciamento de histórico e evento hashchange. Essas duas características permitem aplicações JavaScript muito mais ricas e rápidas. Vamos começar com uma rápida visão do que elas proporcionam.
Hashchange
Esse evento é bem simples. Sempre que a propriedade window.location.hash mudar, ao seguir um link, definindo a propriedade, editando a URL ou navegando pelo histórico para voltar e avançar, o evento hashchange disparado no objeto window. Desse modo, usá-lo é realmente fácil:
window.onhashchange = function() {
alert("hash changed!");
};
window.location.hash = Math.random(); // alerts "hash changed!"
Essa característica está implementada nas versões recentes de todos browsers mais importantes. Em browsers mais antigos, como internet Explorer 6 e 7, você pode facilmente incorporá-la polling a propriedade hash em um intervalo, e disparando manualmente um evento quando ele muda. Isso é fácil de construir em um plugin jQuery, coisa que Ben Alman fez no robusto jquery.hashchange.js plugin.
Gerenciamento do histórico
Esta característica é um pouco mais complexa. Os browsers que a suportam incluem um objeto window.history, com as seguintes propriedades:
- window.history.back() e window.history.forward(), que proporcionam interfaces programáticas para as funções back() e forward() do browser.
- window.history.pushState(stateObj, title, url). Este método inclui uma nova entrada no histórico do browser, que se transforma então no estado atual do browser. Você pode fornecer qualquer JSON-stringifiable object para enviar juntamente com ele, e o browser irá fornecer o objeto novamente quando você navegar para esse ponto (mais sobre isso logo a seguir). Mais importante, se você fornecer uma URL, o browser mudará a URL mostrada na barra de endereços, sem recarregar a página. A mesma URL tem que ser do mesmo domínio, mas você pode mudar o resto, que são a window.location.pathname e window.location.hash. A mudança da URL dessa maneira não disparará um evento “hashchange”.
- window.history.replaceState(stateObj, title, url). Isso equivale a window.history.pushState, exceto que o estado atual do browser é removido do histórico, de forma que você não pode retornar a ele com o “back”.
- window.onpopstate. Este evento é disparado sempre que um state object é removido do histórico do browser, o que acontece ao usar o “back” e o “forward”. Os state objects permanecem no disco rígido entre uma sessão e outra, o que é uma característica interessante. O object que passou em uma chamada para pushState ou replaceState é fornecido como a propriedade state no evento object dentro do evento “popstate”.
Este recurso está implementado nas últimas versões do WebKit, o que inclui o Safari, o Chrome e o Firefox.
Quando usar?
O novo gerenciamento de histórico é muito promissor, porque ele permite que aplicativos web vivam através de muitas URLs físicas, rodando em uma única instância. Isso é importante para alguns tipos de aplicativos, que não têm uma boa experiência ao usar hash.
Por exemplo, no Twitter, atualizamos a URL hash ao navegar pelo aplicativo, para que cada página tenha uma URL específica como http://twitter.com/#replies. Contudo, forçamos um carregamento completo de certas páginas, principalmente páginas de perfis (por exemplo, http://twitter.com/bcherry) e páginas de permalink (por exemplo, http://twitter.com/bcherry/status/18966802499). Isso é para que essas URLs possam ser copiadas da barra de endereços e postadas na web.
Queremos ter certeza de que usuários sem JavaScript e bots de mecanismos de busca enviando links para nosso site obterão a página correta do servidor (desde que o browser não mande junto um hash para o servidor). Isto não seria possível se essas URL usassem hashes. Infelizmente, isso significa um aplicativo mais lento por ser necessário um carregamento completo da página para fora e para dentro dessas locações.
Aqui é que o gerenciamento do histórico do HTML5 pode ser útil.
Então, qual é o problema?
Infelizmente, as implementações existentes de gestão de histórico não são úteis, e não estão no espírito da web.
Nossos aplicativos web devem ser construídos para responder a uma URL. As versões tanto para o cliente quanto para o servidor de um aplicativo devem entender uma estrutura URL compartilhada, e saber como apresentar a mesma página para o browser que reflita aquela URL.
A permissão para que desenvolvedores armazenem informações extras no histórico do browser desvia-se do foco. A única coisa a guardar no histórico deveria ser a URL, e o browser pode associar um título a ela se assim escolher.
Esse é um modelo compatível com REST, que funciona da mesma maneira tanto do lado do cliente quando do servidor. Browsers modernos suportam mudar a URL sem recarregar a página, enquanto os mais antigos continuam a acessar o servidor o tempo todo.
Dessa forma, podemos construir aplicativos que se degradam corretamente em browsers mais antigos e que, quando percebidos por bots, proporcionem uma experiência mais rápida para usuários com browsers mais modernos.
Insira “pathchange”
Tanto “hashchange” quanto pushState/”popstate” devem ser substituídos por “pathchange”, que é um evento disparado quando a URL tem qualquer mudança.
Esse evento não fornece nenhuma informação, e o aplicativo deve inspecionar a URL atual para descobrir o state em que deve entrar. Links relacionados não devem forçar o recarregamento da página, devendo ao invés disso disparar o evento “pathchange”.
Assim é possível implementar esse evento em browsers modernos, com base nas características que eles já têm. Aí vai como:
- Escute o “hashchange”, e dispare o “pathchange” quando ele ocorrer.
- Analise se o navegador não suporta “hashchange” e dispare o “hashchange”, com gatilhos “pathchange”.
- Com suporte do histórico, escute o “popstate” e dispare o “pathchange” quando ele ocorrer.
- Com suporte do histórico, intercepte todos os links relacionados ao serem clicados, e impeça a navegação normal. Em vez disso, chame window.history.pushState(null, null, href) e dispare um “pathchange”.
- Providencie uma função helper para navegar para novas URLs e facilitar, quando suportado, o uso da window.history.pushState.
Implementei isso tudo como um plugin jQuery que é muito fácil de usar:
$(function() {
$.pathchange.init(); // setup event listeners, etc.
$(window).pathchange(function() {
respondToUrl();
}).trigger("pathchange");
$.pathchange.changeTo("/foo");
});
Também criei uma página demo que apresenta um gerenciamento de histórico para aplicativos HTML5 que usa jquery.pathchange.js de forma subjacente. Cheque em vários browsers para ver a mágica do HTML5 trabalhando, certificando-se de usar os botões “back” e “forward”, e de recarregar a página algumas vezes.
Esta é minha opinião quanto aos recursos do histórico do HTML5. Alguns browsers ainda não estão implementando o que realmente precisamos, mas é encorajador que forneçam o necessário para implementar o que realmente precisamos.
Nota – Vale a pena ressaltar que descobri um bug sério na implementação WebKit de gestão do histórico. Resumidamente, o evento “popstate” é frequentemente perdido quando a rede está ocupada, o que não faz sentido. Veja aqui uma página demo com um caso reproduzível, que dispara uma solicitação de download de uma imagem que gasta 1s em cada “popstate”, o que significa que acionar “back” mais de uma vez a cada segundo leva à perda de entradas no histórico, e a um aplicativo que perde a sincronização com a URL. Você pode contornar isso apurando a URL e, adicionalmente, ouvindo o “popstate”, mas não é uma boa prática. Até que isso seja sanado, você deve ficar alerta quanto a isso no caso de fornecer essa característica a seus usuários, que provavelmente não é adequada para apps AJAX muito complexas.
?
Texto original disponível em: http://www.adequatelygood.com/2010/7/Saner-HTML5-History-Management