Fork me on GitHub

Você está aqui: Home Dive Into HTML5


Manipulando Histórico
Para Diversão & Lucro

 

Mergulhando

A barra de endereços do navegador é talvez a peça mais antiga da interface de usuário no mundo. Há URLs em outdoors, nas laterais de trens, e até mesmo em grafites de rua. Combinado com o botão de voltar — provavelmente o botão mais importante do navegador — você tem uma maneira poderosa para avançar e voltar no vasto conjunto de recursos interligados chamado de Web.

A API de histórico da HTML5 é uma maneira padronizada para manipular o histórico do navegador via script. Parte desta API — navegando pelo histórico — está disponível em versões anteriores do HTML. As novas funcionalidades em HTML5 incluem uma maneira de adicionar entradas ao histórico do navegador, para visivelmente alterar a URL na barra de endereços do navegador (sem precisar atualizar a página), e um evento é acionado quando estas entradas são removidas da pilha do navegador quando o usuário pressiona o botão voltar. Isto quer dizer que a URL na barra de endereços do navegador pode continuar seu trabalho de ser um identificador único para o recurso atual, mesmo em aplicações com scripts pesados que nem sempre necessitam de uma atualização na página toda.

O porquê

demon reading book

Por que você iria manipular manualmente a barra de endereços do navegador? Afinal, um simples link pode navegar até uma nova URL; essa é a forma que a web tem funcionado nos últimos 20 anos. E isso vai continuar funcionando desta forma. Esta API não tenta revolucionar a web. Muito pelo contrário. Nos últimos anos, os desenvolvedores web têm encontrado novas e excitantes formas de revolucionar a web sem a ajuda de padrões emergentes. A API de histórico da HTML5 foi na verdade criada para garantir que as URLs continuem sendo úteis em aplicações web com scripts pesados.

Voltando aos princípios, o que uma URL faz? Ela identifica um recurso único. Você pode fazer um link direto para ela; você pode marcá-la; motores de busca podem indexá-la; você pode copiar, colar e enviá-la por e-mail para outra pessoa, esta pessoa pode clicar e acabar vendo o mesmo recurso que você viu originalmente. Estas são todas qualidades excelentes. URLs são importantes.

Então nos queremos que recursos únicos tenham URLs únicas. Mas ao mesmo tempo, navegadores sempre tiveram uma limitação fundamental: Se você mudar a URL, mesmo através de script, ele dispara uma requisição ao servidor web remoto e recarrega toda a página. Isso consome tempo e recursos, e parece um desperdício quando você esta navegando para uma página que é substancialmente semelhante à sua página atual. Tudo que possui na nova página é baixado, até mesmo as partes que são exatamente as mesmas da página atual. Não tem como alterar a URL em um navegador e este fazer download de apenas metade da página.

A API de histórico do HTML5 permite que você faça isso. Ao invés de desencadear uma atualização na página inteira, você pode utilizar o script para, em essência, baixar metade de uma página. Esta ilusão é um truque difícil, e requer algum trabalho da sua parte. Você está prestando atenção?

Magico fazendo um truque de cartas

Digamos que você possui duas páginas, página A e página B. As duas páginas são 90% idênticas; somente 10% do conteúdo destas páginas é diferente. O usuário navega para a página A, então tenta navegar para a página B. Mas ao invés de desencadear uma atualização na página toda, você interrompe o navegador e segue estes passos manualmente:

  1. Carrega os 10% da página através da página B que é diferente da página A (provavelmente utilizando XMLHttpRequest). Isso vai exigir algumas modificações no lado servidor de sua aplicação web. Você vai precisar escrever um código que retorne apenas os 10% da página B que é diferente da página A. Isso pode ser uma URL oculta ou um parâmetro de consulta que o usuário final normalmente não vê.
  2. Troque o conteúdo alterado (utilizando innerHTML ou outro método DOM). Talvez você precise redefinir os manipuladores de eventos que foram alterados junto com o conteúdo.
  3. Atualize a barra de endereços do navegador com a URL da página B, utilizando um método particular da API de histórico do HTML5 que eu já vou mostrar para você.

Ao final desta ilusão (se você a executar corretamente), o navegador acaba com um DOM que é idêntico a página B, como se você tivesse navegado diretamente para a página B. A barra de endereços do navegador acaba ficando com uma URL que é idêntica a da página B, como se você tivesse navegado diretamente para a página B. Mas na verdade você nunca navegou até a página B, e você nunca precisou atualizar toda a página. Esta é a ilusão. Mas porque a página "compilada" se parece exatamente como a página B e possui a mesma URL, o usuário provavelmente nunca notará a diferença (nem te agradecerá por todo trabalho pesado micro gerenciando suas experiências).

O Como

A API de histórico do HTML5 é apenas vários métodos no objeto window.history, mais um evento no objeto window. Você pode utilizar isto para detectar o suporte da API de histórico. O suporte atualmente é limitado para as últimas versões de alguns navegadores, colocando essa técnica diretamente no campo "progressive enhancement".

Suporte para history.pushState
IEFirefoxSafariChromeOperaiPhoneAndroid
·4.0+5.0+8.0+11.50+4.2.1+·

dive into dogs é um exemplo simples, mas não trivial de usar a API de histórico do HTML5. Ele demonstra um padrão comum: um longo artigo com uma galeria de fotos. Em um navegador compatível, navegando pelos links "Próximo" e "Anterior" na galeria de fotos irá atualizar apenas foto no lugar e atualizará o URL na barra de endereços do navegador, sem desencadear uma atualização na página inteira. Em navegadores sem suporte - ou, de fato, navegadores compatíveis onde o usuário tenha desabilitado scripts - os links simplesmente funcionam como links normais, levando você a uma nova página com uma atualização na página toda.

Isso nos leva a um ponto importante:

Professor Marcação diz

Se sua aplicação web falhar em navegadores com script desabilitado, o cachorro de Jakob Nielsen vai até a sua casa e defecará no seu carpete.

O penhor

<aside id="gallery">
  <p class="photonav">
    <a id="photonext" href="casey.html">Next &gt;</a>
    <a id="photoprev" href="adagio.html">&lt; Previous</a>
  </p>
  <figure id="photo">
    <img id="photoimg" src="gallery/1972-fer-500.jpg"
            alt="Fer" width="500" height="375">
    <figcaption>Fer, 1972</figcaption>
  </figure>
</aside>

Nada diferente aqui. A foto em si é uma <img> dentro de uma <figure>, o link é um elemento <a> normal, e a coisa toda está colocada dentro de um <aside>. É importante que estes links regulares realmente funcionem. Todo o código se passa atrás de um script de detecção. Se um usuário estiver utilizando um navegador sem suporte, nada do nosso código chique da API de histórico será executado. E, claro, sempre há alguns usuários com o script desativado por completo.

A função principal pega cada um destes links e repassa para uma função, addClicker(), que faz todo o trabalho de criação e customização do click.

function setupHistoryClicks() {
  addClicker(document.getElementById("photonext"));
  addClicker(document.getElementById("photoprev"));
}

Esta é a função addClicker(). Ela pega um elemento <a> e adiciona um click manipulado. E é neste click manipulado onde as coisas ficam interessantes.

function addClicker(link) {
  link.addEventListener("click", function(e) {
    swapPhoto(link.href);
    history.pushState(null, null, link.href);
    e.preventDefault();
  }, false);
}

 ↜ Interessante

A função swapPhoto() realiza as duas primeiras etapas de nossa ilusão de três etapas. A primeira metade da função swapPhoto() faz parte da URL do link de navegação em si — casey.html, adagio.html, &c. — e constrói uma URL para uma página oculta que contém nada mais que a marcação exigida pela próxima foto.

function swapPhoto(href) {
  var req = new XMLHttpRequest();
  req.open("GET",
           "https://diveintohtml5.com.br/examples/history/gallery/" +
             href.split("/").pop(),
           false);
  req.send(null);

Aqui temos um exemplo de marcação retornado por https://diveintohtml5.com.br/examples/history/gallery/casey.html. (Você pode verificar isso no seu navegador visitando a URL diretamente.)

<p class="photonav">
  <a id="photonext" href="brandy.html">Next &gt;</a>
  <a id="photoprev" href="fer.html">&lt; Previous</a>
</p>
<figure id="photo">
  <img id="photoimg" src="gallery/1984-casey-500.jpg"
          alt="Casey" width="500" height="375">
  <figcaption>Casey, 1984</figcaption>
</figure>

Isso lhe parece familiar? Deveria. Este é a mesma marcação básica utilizada na página original para mostrar a primeira foto.

A segunda metade da função swapPhoto() realiza a segunda etapa de nossa ilusão de três etapas: inserindo a nova marcação baixada dentro da página atual. Lembre-se que tínhamos um <aside> envolvendo todo a figura, foto e legenda. Então inserindo a nova marcação da foto é uma linha, atribuindo a propriedade innerHTML do <aside> para a propriedade responseText retornada do XMLHttpRequest.

 if (req.status == 200) {
    document.getElementById("gallery").innerHTML = req.responseText;
    setupHistoryClicks();
    return true;
  }
  return false;
}

(Observe também a chamada para o setupHistoryClicks(). Isto é necessário para reiniciar os eventos do click manipulados nos novos links de navegação. Atribuindo innerHTML remove qualquer traço dos links antigos e seus eventos.)

Agora, vamos voltar para a função addClicker(). Depois de alterar a foto com sucesso, temos mais uma etapa para a nossa ilusão de três etapas: atribuir a URL na barra de navegação do navegador sem atualizar a página.

A troca

history.pushState(null, null, link.href);

A função history.pushState() recebe três parâmetros:

  1. state pode ser qualquer dado com estrutura JSON. Isso é passado de volta para o manipulador do evento popstate, o qual você irá aprender em algum momento. Nós não precisamos acompanhar nenhum estado nesta demonstração, isto será mantido como null.
  2. title pode ser qualquer string. Este parâmetro não é utilizado atualmente pelos principais navegadores. Se você quiser alterar o título da página, você deve armazenar isto em um argumento state e atribuir isto manualmente no callback do seu popstate.
  3. url pode ser, bem, qualquer URL. Isto é a URL que você gostaria que aparecesse na barra de endereços do navegador.

Ao chamar history.pushState mudará imediatamente a URL na barra de endereços do navegador. Então, este é o final da ilusão? Bom, não exatamente. Nós ainda precisamos falar sobre o que acontece quando o usuário pressiona o importantíssimo botão de voltar.

Geralmente quando um usuário navega a uma nova página (com uma atualização de página inteira), o navegador joga a nova URL para a lista de histórico, faz os downloads e desenha a nova página. Quando o usuário pressiona o botão voltar, o navegador joga uma página para fora da pilha de histórico e redesenha a página anterior. Mas o que acontece agora que você gerou um curto-circuito de navegação para evitar a atualização da página toda? Bom, você fingiu o "avançar" para uma nova URL, então agora você pode também fingir um "voltar" para a URL anterior. E a chave para forjar este "retorno" é o evento popstate.

O prestígio

window.addEventListener("popstate", function(e) {
    swapPhoto(location.pathname);
}

Após você ter utilizado a função history.pushState() para jogar a URL para o histórico do navegador, quando o usuário pressionar o botão voltar, o navegador irá disparar um evento popstate no objeto window. Esta é a sua chance de completar a ilusão de uma vez por todas. Porque fazer alguma coisa desaparecer não é o suficiente; você tem que trazer ela de volta.

Nesta demonstração, “trazer de volta” é tão simples quanto trocando a foto original, que nós fazemos ao chamar o swapPhoto() com a localização atual. Quando o retorno do popstate é chamado, a URL visível na barra de endereço do navegador foi trocada pela URL antiga. E também, a propriedade location global já foi atualizada com a antiga URL.

Para ajudar a visualizar isso, vamos percorrer a ilusão inteira desde do começo ao fim:

A ilusão está completa. Todas as evidências visíveis (o conteúdo da página e a URL na barra de endereços) sugerem ao usuário que ele navegou uma página a frente e uma para trás. Mas nenhuma página foi completamente atualizada - isto foi tudo uma ilusão meticulosamente executada.

Leitura complementar

Este foi “Manipulando Histórico Para Diversão & Lucro” O Sumário tem muito mais, se você quiser continuar a leitura.

Você sabia?

Em associação a Google Press, O’Reilly está distribuindo este livro em variados formatos, incluindo papel, ePub, Mobi, DRM-free e PDF. A edição paga é chamada “HTML5: Up & Running,” e está disponível agora.

Se você gostou dessa introdução e quer mostrar como apreciou, basta comprar o livro “HTML5: Up & Running” com esse link afiliado ou comprar a edição eletrônica diretamente da O’Reilly. Você vai ganhar um livro, e eu vou ganhar um trocado. Atualmente não aceito doações diretas.

Copyright MMIX–MMXI Mark Pilgrim