Histórico para Navegação via JavaScript
Já foram criadas muitas soluções para consertar o histórico quando fazemos a navegação via JavaScript (o maior problema com Ajax): a do Cau Guanabara, a minha, a do Bermonruf, além de outras derivações juntando pedaços das três. Agora eu volto aqui não para criar uma função nova, mas sim explicar os passos para você criar a sua própria função, atendendo às suas próprias necessidades.
Vamos resolver o problema por partes, na seguinte ordem:
Vale lembrar também que estou escrevendo códigos para cuidar do histórico para JavaScript, e não só Ajax (aliás, todas as soluções anteriores faziam isso, mas pouca gente se tocou).
Código Base
Usaremos a seguinte página-base para testarmos nosso script:
<html>
<head>
<title>BackButton Fixes</title>
<script>
var appHash = ""; // Não se preocupe com essa linha agora...
</script>
</head>
<body>
<select id="select">
<option value="option-0">-- Change --</option>
<option value="option-1">Option 1</option>
<option value="option-2">Option 2</option>
</select>
</body>
</html>
Como testaremos a navegação somente com JavaScript, não precisaremos fazer qualquer requisição Ajax. Portanto, não precisaremos de uma função para tal.
Bookmarking
Como na navegação via JavaScript não mudamos a URL, não podemos fazer bookmarking, já que a aplicação, sempre que acessada, estará no estado inicial. A única maneira de permitir o bookmarking seria mudando a URL.
Como sempre estaremos na mesma página, acrescentaremos apenas um fragmento após o nome do arquivo. Poderíamos optar pelas query strings (file.ext?param=value), mas elas atualizariam a página quando fossem mudadas. Então optaremos pelos hashes (file.ext#hash), que não atualizam a página e podem ser mudados à vontade.
Primeiramente, quando a página é carregada, precisamos verificar se houve bookmarking:
function initialize() {
var browserHash = window.location.hash.substring(1); // Hash da barra de navegação
browserHash = (!browserHash) ? 0 : browserHash; // Hash padrão
$("select").selectedIndex = browserHash;
}
Não se esqueça de modificar a tag body para <body onload="initialize()"> (nos preocuparemos com obstrusão depois), e de criar a função $() para pegar elementos pela ID.
Se especificarmos um hash “1″ ou “2″, nosso select mudará quando atualizarmos a página, ele não mudará automaticamente. Mas já temos nosso bookmarking inicial.
Por enquanto, quando mudamos nosso select, a URL não muda. Isso significa que nosso usuário terá que adivinhar qual hash deverá usar, o que não é bom. Precisamos de uma função que mude o hash do navegador quando mudarmos as opções do select:
function makeBookmark() {
window.location.hash = "#" + $("select").selectedIndex; // Muda o hash da página
}
E também devemos modificar nosso select para <select id="select" onchange="makeBookmark()">. Agora, toda vez que trocamos as opções do select, o hash do navegador muda para podermos fazer o bookmark mais tarde.
Até o presente momento, tudo funcionando no IE, no FF e no Opera. Note que no FF e Opera o histórico já é ativado, embora não faça efeito.
Histórico
Atualmente, os browsers não disparam evento algum quando voltamos ou avançamos no histórico. Sendo assim, não existe nenhuma maneira “automática” que nos ajude a verificar quando navegarmos pelo histórico.
Para fazer a verificação, executamos uma função em um curto intervalo de tempo. Essa função verifica se o hash do browser se refere ao estado da aplicação. Se for falso, faz as modificações necessárias.
Vamos começar fazendo as alterações necessárias para a função initialize:
function initialize() {
handleHistory(); // Verificação inicial
window.setInterval(handleHistory, 100); // Intervalo para verificação
}
Essa é a função que faz a verificação inicial e “agenda” as próximas, a cada 100 milissegundos. A função que faz a verificação é a seguinte:
function handleHistory() {
if(window.location.hash != appHash) { // Verificação de Estado
appHash = window.location.hash; // Trocamos o estado da aplicação
$("select").selectedIndex = window.location.hash.substring(1); // Trocamos a opção selecionada
}
}
Ela compara o hash da aplicação (que é o appHash, variável global lá do início do nosso script) com o hash do browser. Se forem diferentes, faremos as adaptações, em nosso caso seria a mudança das opções do select.
Outra modificação necessária em nosso script será a função makeBookmark, que agora se chamará makeHistory:
function makeHistory(newHash) {
window.location.hash = "#" + newHash;
appHash = newHash;
}
Ela será executada toda vez que mudarmos as opções do select. O select também deverá ser mudado para <select id="select" onchange="makeHistory(this.selectedIndex)">.
Note que temos alguns problemas: quando no estado inicial, não temos diferença entre appHash e o hash do browser, fazendo com que nada aconteça (causa problemas com o uso de Ajax), e ainda não temos o funcionamento desejado nem no IE nem no Opera.
Correções para o IE
O IE sempre foi do contra, e ele não abriu excessão para nossa técnica anterior. Quando trocamos o hash da página, o IE não ativa o histórico, e não há maneira de contornar isso sem a gambiarra. Essa gambiarra seria o uso de um iframe escondido, do qual falaremos daqui a pouco.
Como você já deve ter entendido o espírito da coisa, vou postar o código inteiro, e vou explicando as linhas que forem necessárias, até porque alguém pode ter se perdido.
var appHash = "";
var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") > -1) ? true : false;
function $(id) { return document.getElementById(id); }
function makeHistory(newHash) {
if(isIe === true)
$("control-iframe").setAttribute("src", "control.htm?id=" + newHash);
window.location.hash = "#" + newHash;
appHash = newHash;
}
function handleHistory() {
var browserHash = (hash = window.location.hash) ? hash : "#" + 1;
if(browserHash != appHash) {
appHash = browserHash;
$("select").selectedIndex = browserHash.substring(1);
}
}
function createIFrame() {
var iFrame = document.createElement("iframe");
iFrame.setAttribute("src", "control.htm?id=1");
iFrame.setAttribute("id", "control-iframe");
iFrame.style.display = "none";
document.body.appendChild(iFrame);
}
function initialize() {
if(isIe === true)
createIFrame();
handleHistory();
window.setInterval(handleHistory, 100);
}
Vamos pela ordem. Na função initialize, criamos um iframe escondido se o browser for o IE (definido pela variável isIe, lá no começo), através da função createIFrame, sem mistério.
Na função makeHistory, trocamos a URL do iframe (se for o caso), forçando sua atualização (veremos porque mais tarde).
No iframe, temos o seguinte código:
<html><head><script type="text/javascript">
frameUrl = window.location.search.substring(4);
parentHash = (typeof(hash = parent.location.hash.substring(1)) != "undefined" ? hash : "");
if(frameUrl != "")
parent.location.hash = frameUrl;
</script></head><body></body></html>
Toda vez que o iframe é atualizado (seja pelo usuário ou pela navegação no histórico), ele muda o hash da página pai, nos permitindo fazer a verificação de estados.
Também fiz uma pequena modificação: estado inicial (caso não haja um hash) para o FF/Opera. Na parte do IE já fiz direto.
Aperfeiçoamentos
Para facilitar o uso, e por causa de alguns bugs existentes, fiz algumas modificações no código ao longo do tempo. Não irei explica-las agora, isso fica para uma outra oportunidade.
O código completo do script, corrigido e atualizado, é esse:
var appHash = "";
var appIndex = "";
var appDefault = "home";
var appFunction = function(){};
var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") > -1) ? true : false;
function $(id) { return document.getElementById(id); }
function makeHistory(newHash) {
if(isIe === true)
$("control-iframe").setAttribute("src", "control.htm?id=" + newHash);
else if(newHash != appIndex)
window.location.hash = newHash;
else
go2index();
}
function handleHistory() {
var browserHash = (hash = window.location.href.split("#")[1]) ? hash : "";
if(browserHash != appHash) {
if(browserHash != "") {
appFunction(browserHash);
appHash = browserHash;
makeHistory(browserHash);
}
else {
clearInterval(checkInterval);
window.location.hash = appHash;
makeHistory(appIndex);
}
}
}
function createIFrame() {
var iFrame = document.createElement("iframe");
iFrame.setAttribute("src", "control.htm?id=" + (hash = window.location.href.split("#")[1]) ? hash : appDefault);
iFrame.setAttribute("id", "control-iframe");
iFrame.style.display = "none";
document.body.appendChild(iFrame);
}
function initialize() {
if(isIe === true)
createIFrame();
checkBookmark();
checkInterval = setInterval(handleHistory, 100);
}
function checkBookmark() {
var browserHash = (hash = window.location.href.split("#")[1]) ? hash : "";
window.location.hash = (browserHash == "") ? appDefault : browserHash;
}
function go2index() {
window.location = window.location.href.split("#")[0] + "#";
window.location.reload();
}
function goTo(strUrl) {
window.location.hash = strUrl;
appFunction(strUrl);
}
E o código do iframe control.htm é:
<html><head><script type="text/javascript">
frameUrl = window.location.search.substring(4);
parentHash = (typeof(hash = parent.location.href.split("#")[1]) != "undefined" ? hash : "");
if(parentHash != "" && frameUrl == parent.window.appIndex)
parent.window.go2Index();
else if(frameUrl != "" && frameUrl != parent.window.appIndex)
parent.location.hash = frameUrl;
</script></head><body></body></html>
Pra usar a função, você faz sua página normalmente. Você precisará criar um arquivo com todo o script acima, e inclui-lo em sua página. Você também precisará criar o arquivo control.htm, o nosso iframe.
No script, você poderá mudar as variáveis appDefault (hash inicial), appIndex (hash que deve atualizar a página) e appFunction (função que faz a navegação). Para executar sua função de navegação, chame a função makeHistory, passando a query string como parâmetro.
Simples, não? Não, mas é fácil de usar. Dúvidas, bugs, sugestões, qualquer coisa que possa ajudar, é só comentar!
Créditos
Não criei tudo isso do nada, me baseei em algumas idéias já existentes. As principais foram de Mike Stenhouse (idéia original), Bernardo Rufino (aperfeiçoamentos), Dekassegui (correções e personalizaçãoes), e um pouco da minha idéia quase-original também (que não me lembro o nome do sujeito que tinha postado um rascunho em um fórum por aí).







Muito bom Julio, muito bom mesmo.
Júlio, muito bom! Parece que sabia o que estava fazendo, há algum tempinho venho esboçando algo mais organizado, nada de novo, para este esquema da navegação…
É, parti da sua base. Tirei algumas coisas a mais (no seu script o iframe era criado mesmo no FF), alterei outras… Mas tem coisa que depende de quem programa mesmo.
Ah, e valeu pelos elogios!
Fala, Julio.
Faz tempo que venho procurando uma solução para Histórico de Navegação para JavaScript/Ájax.
Entendi o conceito como um todo, mas não entendi como utiliza-la na minha página HTML por exemplo:
Link 1
Gostaria de gerar histórico para document.getElemntById(‘div_teste’). innerHMTL = ‘xxxxxx’ para por navegar na mudança de conteúdo do div_teste.
Como utilizo ?
Obrigado.
Por favor seria possivel executar uma funcão javascript sendo ela o hash de URL como esse exemplo, mas usando sua função
http://www.akmattie.net/blog/dshistory/usage.html
se puder colocar um exemplo do funcionamento, desde já agradeço
Sim, seria possível. Precisaria apenas de uma pequena mudança no script. Basta usar eval na função que é executada quando o histórico muda. E não, não tenho exemplo, vou tentar providenciar até o final de semana.
Muito bom…
Lendo esse tutorial tentei fazer com que o javascript me retornasse o endereço que está na barra do navegador.
Até agora só consegui printar o domínio aonde o arquivo está hospedado….
É possível?
Como seria?
Forte abraço!
Afinal!!! Com aplico isso????
Cara, meus parabéns!
Uma solução incirvél, já passei a utilizar junto com meu ajax.
@Danilo: basta incluir o script em seu documento, setar a variável appFunction para a função que você deseja usar para fazer as requisições, e, no window.onload, usar o initialize
@Deusimar: Obrigado, se tiver sugestões/correções/reclamações, sinta-se à vontade!
Muito Legal!
Não é que funcionou!!!
Só uma coisa.
Porque que quando “dou” F5 no IE, ele acrescenta no título da página o nome do arquivo carregado dentro da div.
No FF e Ópera não acontece isso!!!
A alguns dias procurava por isso, li uma explana;áo em outros lugares mas só aqui compreendi claramente, parabéns pelo dicernimento e obrigado.
Porque que quando “dou” F5 no IE, ele acrescenta no título da página o nome do arquivo carregado dentro da div. [2]
Ola, Julio, como faço para mim alertar o nome escrito na barra de endereços depois do # ?
Como faço esse script funcionar usando o que esta digitado no hash?
Obrigado
Bom, eu fiz um para interagir com o Flash, inclusive a classe pra AS3, qualquer hora dessas meu e-mail está aí podemos trocar idéias sobre isso e incrementar, ahh o que fiz em javascript é em classe, e não há a necessidade de criar um novo documento (control.html) para gerar o historico no IE vlw
Olá a todos!
@Julio Greff
Ouh Júlio. Obrigado por publicar seu trabalho! É genial esse script e o conceito e tal.
Achei seu site pelo link no site do Bernardo Rufino. ‘Enxertei’ o script dele no meu site há algum tempo, mas não consegui entender exatamente como funciona. (O código dele está muito ‘alienígena’ hehehehe, com “funções-inline” e “prototypes” que eu não entendo direito ainda, na minha ignorância)
Eu quis fazer um “downgrade” no código do Bernardo. Não queria passar o nome da função depois do hash, só o argumento. Mas não consegui ‘editar’ o script dele pra isso. Então resolvi estudar a sua versão, pra ver se eu ‘pegava a idéia’ melhor. No momento estou com as duas versões “enxertadas” e funcionando aqui no meu framework pessoal. Obrigado a vocês dois !! (só tem um probleminha que eu comento a seguir…)
@todos
Estou tendo um problema com esse script no Ópera. O funcionamento da função voltar (tanto pelo botão no menu quanto pelo atalho ‘Alt+seta’) está intermitente; preciso clicar 2 ou 3 vezes até funcionar. Alguém solucionou, teve ou notou esse problema?
@André (último a postar…)
Ouh cara, eu queria muito estudar a sua ‘classe’ e a interface para ‘ActionScript 3′. Poderia me mandar? Qual seu e-mail? (o meu é agbottan@gmail.com)
Primeiramente: Parabéns aos idealizadores da resolução deste problema, foi a minha salvação e de mais algumas dezenas de WebFazTudo por ai, acredito eu.
Efetuei testes com a implementação do Bernardo e até que funcionou, porém se colocarmos para rodar junto com jQuery a implementação dele não funciona, pelo menos pra mim apresentou alguns problemas, os quais não consegui solucionar.
Foi então que dei mais uma pesquisada e encontrei esta implementação, que também apresenta falhas ao juntar com jQuery. Pois bem, estudando as funções logo encontrei o que dava o conflito: a função “$()”, pois as chamadas de jQuery utilizam o “$” e bla bla bla… o que fiz foi trocar o nome desta função, pra qualquer coisa (no meu caso coloquei “function RRR()” e funcionou 100%.
O problema que encontrei mais precisamente foi usando o Demo #2 deste Menu, faça o teste e verifique se fica tudo certo!
[]’s
Para que tudo isso?
eh so escrever no topo da pagina:
e qualquer mudanca de hash ficara no historico!
Rodrigo, nao entendo, para que usar jQuery???
sou ignorante nao sei para que usar jQuery, (e outras bibliotecas) para mim parece algo que tenta facilitar, mas no final so termina complicando….
alem de ficar muito lento o carregamento do site, final o arquivo sao varios kbs…. sou muito mais usar aplicativos independentes
Olá, Julio. Descobri este site com suas dicas preciosas no Google, para resolver uma dificuldade minha.
Eu queria criar um esquema de navegação que fizesse páginas como as do Orkut, tipo: Main#Home, Main#Produtos, Main#Servicos, ….. mas a navegação com o Ajax deixa falhas, como o botão voltar desabilitado.
Vou testar seu código e depois eu posto aqui o resultado obtido ou as minhas dificuldades em utilizar estes códigos complexos.
Mas desde já eu deixo os parabéns, porque parece um código bastante difícil e que exige grande conhecimento.