Pesquisei e quebrei a cabeça para achar essa solução… tanto que questionei entre dar apenas o ‘mapa’ ou o script pronto. Mas sou um cara open-source por natureza, por isso vou fazer as duas coisas.
Bem, todos já sabemos que comandos em javascript não ativam os botões de histórico do navegador, mesmo que mudem 100% do conteúdo exibido. Isso não é legal, pois sabemos que o usuário busca usar os recursos que conhece e o botão ‘Voltar’ é uma instituição, existe desde que o mundo é mundo. Eu me sentia meio que ‘na obrigação’ de possibilitar isso ao usuário.
Pensei então, há alguns meses em uma solução – que me serviu no momento, mas que não ativava os botões, apenas criava um ‘histórico de comandos’ num array, com funções para adicionar, exibir e testar itens do histórico. Com isso eu fazia os meus próprios botões de histórico. Achei que fosse suficiente, mas hoje vejo que não, pois na maior parte dos casos o usuário nem entende que há um histórico ‘paralelo’ e que ele precisa clicar em outros botões para navegar…
Veja o script original:
// histórico de comandos – 2006-06-07
_ajax.history = {
‘items’: [],
‘currentIndex’: 0,
‘go’: function(ref) {
if(_ajax.history.items[_ajax.history.currentIndex + ref] false)
_ajax.history.currentIndex += ref;
else return;
new Function(_ajax.history.items[_ajax.history.currentIndex])();
},
‘add’: function (str) {
if(str == _ajax.history.items[_ajax.history.currentIndex]) return;
_ajax.history.currentIndex = _ajax.history.items.length;
_ajax.history.items.push(str);
},
‘hasBack’: function() {
return (_ajax.history.items[_ajax.history.currentIndex - 1] || false) ? true : false;
},
‘hasForward’: function() {
return (_ajax.history.items[_ajax.history.currentIndex + 1] || false) ? true : false;
}
};
Caí de cabeça atrás da solução, que eu já sabia, tinha algo a ver com a propriedade hash do objeto location. Descobri algumas possibilidades, a começar pelos famosos frameworks (que eu abomino…), algumas funções interessantes que só funcionavam em Mozilla, outras gigantescas demais para o meu gosto. Tentei então entender como as coisas eram feitas e fazer eu mesmo. Este é sempre o método mais enriquecedor (mas nem sempre é o mais eficiente… é bom lembrar -rsrsrs).
Entendendo
O objeto window.location tem uma propriedade chamada ‘hash’, que mostra ou define aquela parte da URL que vem no final, depois do caracter ‘#’ – que usamos para navegar para diferentes pontos na mesma página, com, por ex.:
<a name="meu_hash"></a> e chamamos com: <a href="#meu_hash">meu_hash</a>
Pois é, ele é a chave para o histórico do navegador, pois ao mudar o hash, mudamos a URL. Então se ao executar um comando, eu mudar o hash o navegador armazena como uma nova página? A resposta é não. Se não há um clique explícito do usuário em um link como o descrito acima (com um A NAME="…"), ao clicar no botão Voltar, nada acontece! O IE nem habilita o botão!!! Bem, mas a URL está diferente e é isso que vamos usar. Usaremos uma variável para armazenar o hash e um setInterval para testá-lo a intervalos regulares. Dê uma olhada nesse link: http://juliogreff.wordpress.com/2006/10/20/ativando-o-botao-voltar/.
Com o script que o Júlio mostra, resolve-se a questão. Mas resta um problema: o IE não habilitou o botão voltar apenas com a mudança do hash… Mais pesquisas e mais pesquisas para achar a solução, que quando foi encontrada me pareceu tão óbvia que não sei por que não pensei nisso antes (sempre pensamos isso, né?). Quando carregamos um frame ou iframe o IE cria um novo item no histórico! Veja a página de onde tirei a idéia: http://www.contentwithstyle.co.uk/Articles/38/fixing-the-back-button-and-enabling-bookmarking-for-ajax-apps.
Esse exemplo é crossbrowser, mas muito complicado. Serviu para esclarecer, mas não tive vontade de usá-lo.
Bem, baseado nesses scripts resolvi criar um terceiro, fundindo com o que já tinha pronto (mostrado acima) criei o objeto ajaxHistory. Vou falar sobre a lógica que usei antes de mostrar o script. Basicamente preservei os métodos e propiedades do antigo objeto e acrescentei novos métodos para permitir o uso do objeto history do navegador.
Em nosso objeto haverá um array para armazenar os comandos (como strings) chamado ajaxHistory.items e um inteiro que indica o índice do item exibido no momento, ajaxHistory.currentIndex. O sistema original funciona da forma mais simples possível. A cada comando que modifique o conteúdo da página, executamos ajaxHistory.add enviando uma string que se executada como comando reproduz a operação que modificou a página. Depois podemos usar os métodos ajaxHistory.go ou ajaxHistory.goTo para navegar pelos itens do nosso ‘histórico de comandos’. Além do método goTo, o array ajaxHistory.captions foi adicionado, possibilitando ajustar o título da página de acordo com o item exibido. Aí podemos usar ajaxHistory.hasBack e ajaxHistory.hasForward para testar se há itens atrás ou adiante do atual e assim habilitar ou desabilitar nossos botões personalizados de histórico.
Ok, mas e para usar os benditos botões de histórico do browser? Para isso vários métodos foram criados, mas para executar, basta chamar o método ajaxHistory.activateBrowserHistory no onload da página, uma única vez. Isso dispara uma série de eventos que em conjunto habilitam o histórico do navegador. Vamos a eles.
A lógica e a execução da coisa são diferentes para Mozilla e IE (novidade…).
Em ambos os casos o hash é modificado ao criar um novo item ou ao carregar um item do histórico (ajaxHistory.setHash).
No Mozilla criamos um setInterval chamando o método ajaxHistory.checkHash a cada 1/10 de seg. testando se o hash é igual a ajaxHistory.currentIndex, se for diferente, o usuário clicou no botão voltar (ou avançar). O hash é setado com o valor do índice do item no array ajaxHistory.items, então ajaxHistory.goTo é chamado com o hash como parâmetro.
No IE um iframe é inserido na página, com display:none. Este iframe carrega o arquivo ‘historyframe.html’ que deve ser criado como descrito abaixo:
function checkHistory() {
var pi = parent.location.hash.replace(/#/,’’), si = location.href.replace(/^.+\?/,’’);
if(si && pi && (parent.ajaxHistory || false))
if(pi != si) parent.ajaxHistory.goTo(si);
}
</script></head><body onLoad="checkHistory();"></body></html>
No caso do IE, ajaxHistory.setHash recarrega o iframe enviando na query string o valor do índice, assim: ‘historyframe.html?2′, por exemplo. O arquivo ‘historyframe.html’ ao ser carregado testa o índice enviado na URL com o hash na URL da janela principal, se forem diferentes, ajaxHistory.goTo é chamado com o índice enviado na URL como parâmetro.
O Mozilla, ao contrário do IE, habilita os botões mas não há ação mesmo quando o iframe é recarregado, portando os dois métodos foram necessários. Bem, lá vai o script:
* ajaxHistory – histórico de comandos para navegação por Ajax
* ———————————————–
* autor: Cau Guanabara
* data: 2006-06-07 :: 2006-11-07
* ———————————————–
*/
var ajaxHistory = {
‘items’: [],
‘captions’: [],
‘currentIndex’: 0,
‘go’: function(ref) {
if(this.items[this.currentIndex + ref] || false) this.currentIndex += ref;
else return;
if(this.activeHistory) this.setHash();
if(this.captions[this.currentIndex]) document.title = this.captions[this.currentIndex];
new Function(this.items[this.currentIndex])();
},
‘goTo’: function(ind) {
if(this.items[ind] || false) this.currentIndex = ind;
else return;
if(this.activeHistory) this.setHash();
if(this.captions[this.currentIndex]) document.title = this.captions[this.currentIndex];
new Function(this.items[this.currentIndex])();
},
‘add’: function(str, cap) {
if(str == this.items[this.currentIndex]) return false;
this.currentIndex = this.items.length;
this.items.push(str);
this.captions.push(cap || false);
if(this.activeHistory) this.setHash();
return true;
},
‘hasBack’: function() { return (this.items[this.currentIndex - 1] || false) ? true : false; },
‘hasForward’: function() { return (this.items[this.currentIndex + 1] || false) ? true : false; },
‘activeHistory’: false,
‘activateBrowserHistory’: function() {
if(document.all) this.makeIframe();
else setInterval("ajaxHistory.checkHash()", 100);
this.activeHistory = true;
},
‘makeIframe’: function() {
this.iframe = document.createElement(’iframe’);
this.iframe.style.display = ‘none’;
this.iframe.src = ‘historyframe.html’;
document.getElementsByTagName(’body’)[0].appendChild(this.iframe);
},
‘checkHash’: function() {
var curHash = location.hash.replace(/#/,’’);
if(this.currentIndex != curHash) this.goTo(curHash);
},
’setHash’: function() {
top.location.hash = this.currentIndex;
if(document.all)
this.iframe.contentWindow.location.href = ‘historyframe.html?’+this.currentIndex;
}
};
Note que este objeto não deve ser inicializado com o operador new, basta chamar os métodos diretamente. Veja abaixo um exemplo de uso do sistema (os comandos no exemplo não mudam o conteúdo da pagina, mas isso não faz a menor diferença – qualquer comando javascript é válido)
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type="text/javascript" language="javascript" src="ajax_history.js"></script>
<script type="text/javascript" language="javascript">
function request(command, caption) {
ajaxHistory.add(command, caption);
document.title = caption;
new Function(command)();
}
</script>
</head>
<body onload="ajaxHistory.activateBrowserHistory();">
Clique nos links e depois navegue pelo histórico<br /><br />
<p><a href="javascript://" onclick="request(’alert(\’link 1\’);’,’link 1′)">link 1</a><br />
<a href="javascript://" onclick="request(’alert(\’link 2\’);’,’link 2′)">link 2</a><br />
<a href="javascript://" onclick="request(’alert(\’link 3\’);’,’link 3′)">link 3</a> </p>
</body>
</html>
Espero que façam proveito das informações contidas aqui. É isso aí.
não consegui fazer rodar o código… diz que ajaxHistory não esta definido. Pode me ajudar?
O sistema do worpress modifica as aspas por acentos ou sei lá o que… será preciso substituir as aspas. Deve ser isso
Não consegui, já fiz a troca e testei… nada!
Devo estar me confundindo na hora de aspas duplas e simples…
Tem como colocar pra download ou mandar por email?
Obrigado!
Coloquei para download aqui:
http://cauguanabara.jsbrasil.com/scripts/ajax_history.js
agora funcionou redondo! Valeu pela ajuda, obrigado!
Abraços!
Olá…
Estou precisando bastante de uma solção para para esse problema.
Baixei o código da sua solução e coloquei seguinte trexo:
… onclick=”request(’document.getElementById(\’teste\’).innerHTML = \’xxxxxx\’;’,’teste’);” …
ele altera a url ai eu avanço de página quando volto a url lá é a mesma mais não aparece o conteudo.
o que aconteçe ???
Obrigado.
No Firefox esta funcionando no IE não.
Alguma dica ?
Obrigado.
Comigo só funcionou no FF… se alguem souber pq me ajudem por favor…
Coloquei um exemplo de funcionamento no ar:
http://cauguanabara.jsbrasil.com/scripts/historybuttons/
Testado em IE7 e FF2
Parabéns pelo dica!
Realmente sensacional!
O único probleminha é que no IE o script não carrega as imagens da div… no FF funciona perfeitamente.
Como eu poderia fazer pra arrumar esse bug no código?
Brigadão!
Olá.
Apliquei certinho tudo que foi passado aqui. Porém ao aplicar a funcao no flash (getURL) nao funcionou direito no ie6.
Veja o exemplo em http://www.webgreat.com.br/ajaxHistory
Arquivo FLA em ajaxHistory/teste.zip
IE7 e FF está tudo ok.
Alguma solução?
Acho q a solução é trocar do IE6 pro IE7.. ou pro FF
=D
Faltava o exemplo, mas achei no comentários… Excelente post!!!
Cau,
Eu te amo!!!!
Eu estava procurando por esse solução há um tempão e já estava até abandonando essa alternativa por conta desse problema com o histórico. Agora vou começar a adaptar o seu script para que eu possa fazer o bookmark com uma alteração na parte de mudança do hash. Eu tenho que colocar no hash informações suficientes para que eu possa saber onde o usuário está.
Não sei se você me entendeu.
Abraço,
Clauber.
Valeu Clauber!
Mas antes de sair festejando, dê uma olhada, pois esse script já rodou por aí e se modificou um bocado, acredito que para melhor!
veja:
http://juliogreff.blog.br/ativando-o-botao-voltar/
http://bermonruf.wordpress.com/2006/11/24/solucao-simples-para-o-botao-voltar-cross-browser/
Valeu Julio! Valeu Bernardo!
Fantástica essa solução! Era exatamente o que eu precisava saber. Simples de usar, leve, prática… Realmente, excepcional.
De fato, o único empecilho que tive na hora de usar o código foi o fato de alguns caractéres do código terem sido substituídos na postagem, mas nada que um “Find and Replace” não resolvesse.
Parabéns pelo artigo!
Deixa eu ressuscitar a conversa.
Não entendo muito de programação pra net, mas olhei o código, ele é Lindo!!! E pelos comentários, ele funciona. Só que comigo não.
Eu havia feito as páginas a serem abertas em .txt e chamava com ajax.
Enfim, ja tentei de tudo, mas não vai, ele da a mensagem de erro programada no código “houve algum problema”.
Estou testando em http://www.rioachei.com/teste/
Desde ja agardeço a atenção.