(Observação: conforme o feedback de Sharky, incluímos código para detectar backspaces)
Então, eu já vi essas perguntas frequentemente no SO e recentemente me deparei com a questão de controlar a funcionalidade do botão Voltar. Depois de alguns dias procurando a melhor solução para meu aplicativo (página única com navegação Hash), criei um sistema simples, sem navegador, sem biblioteca para detectar o botão voltar.
A maioria das pessoas recomenda o uso de:
window.onhashchange = function() {
//blah blah blah
}
No entanto, essa função também será chamada quando um usuário usar no elemento in-page que altera o hash do local. Não é a melhor experiência do usuário quando o usuário clica e a página retrocede ou avança.
Para dar uma visão geral do meu sistema, estou preenchendo uma matriz com hashes anteriores à medida que meu usuário se move pela interface. Parece algo como isto:
function updateHistory(curr) {
window.location.lasthash.push(window.location.hash);
window.location.hash = curr;
}
Bem direto. Faço isso para garantir o suporte entre navegadores e para navegadores mais antigos. Simplesmente passe o novo hash para a função, que será armazenada para você e depois altere o hash (que é colocado no histórico do navegador).
Também utilizo um botão voltar na página que move o usuário entre as páginas usando a lasthash
matriz. Se parece com isso:
function goBack() {
window.location.hash = window.location.lasthash[window.location.lasthash.length-1];
//blah blah blah
window.location.lasthash.pop();
}
Portanto, isso fará com que o usuário volte ao último hash e remova esse último hash da matriz (não tenho botão de avanço no momento).
Assim. Como detecto se um usuário usou ou não o botão voltar da página ou o botão do navegador?
No começo, olhei window.onbeforeunload
, mas sem sucesso - isso só é chamado se o usuário mudar de página. Isso não acontece em um aplicativo de página única usando a navegação por hash.
Então, depois de mais algumas pesquisas, vi recomendações para tentar definir uma variável de flag. O problema com isso no meu caso é que eu tentaria defini-lo, mas como tudo é assíncrono, nem sempre seria definido a tempo para a instrução if na alteração de hash. .onMouseDown
nem sempre era chamado em clique, e adicioná-lo a um onclick nunca o acionava com rapidez suficiente.
Foi quando comecei a olhar para a diferença entre document
, e window
. Minha solução final foi definir o sinalizador usando document.onmouseover
e desativá-lo usando document.onmouseleave
.
O que acontece é que, enquanto o mouse do usuário está dentro da área do documento (leia-se: a página renderizada, mas excluindo o quadro do navegador), meu booleano está definido como true
. Assim que o mouse sai da área do documento, o booleano muda para false
.
Dessa forma, eu posso mudar meu window.onhashchange
para:
window.onhashchange = function() {
if (window.innerDocClick) {
window.innerDocClick = false;
} else {
if (window.location.hash != '#undefined') {
goBack();
} else {
history.pushState("", document.title, window.location.pathname);
location.reload();
}
}
}
Você observará a verificação #undefined
. Isso ocorre porque, se não houver histórico disponível na minha matriz, ele retornará undefined
. Eu uso isso para perguntar ao usuário se ele deseja sair usando um window.onbeforeunload
evento.
Portanto, para resumir, e para pessoas que não estão necessariamente usando um botão voltar da página ou uma matriz para armazenar o histórico:
document.onmouseover = function() {
//User's mouse is inside the page.
window.innerDocClick = true;
}
document.onmouseleave = function() {
//User's mouse has left the page.
window.innerDocClick = false;
}
window.onhashchange = function() {
if (window.innerDocClick) {
//Your own in-page mechanism triggered the hash change
} else {
//Browser back button was clicked
}
}
E aí está. uma maneira simples e em três partes de detectar o uso do botão voltar versus elementos da página no que diz respeito à navegação por hash.
EDITAR:
Para garantir que o usuário não use backspace para acionar o evento back, você também pode incluir o seguinte (obrigado a @thetoolman nesta pergunta ):
$(function(){
/*
* this swallows backspace keys on any non-input element.
* stops backspace -> back
*/
var rx = /INPUT|SELECT|TEXTAREA/i;
$(document).bind("keydown keypress", function(e){
if( e.which == 8 ){ // 8 == backspace
if(!rx.test(e.target.tagName) || e.target.disabled || e.target.readOnly ){
e.preventDefault();
}
}
});
});