Altere o URL no navegador sem carregar a nova página usando JavaScript

297

Como eu teria uma ação JavaScript que pode ter alguns efeitos na página atual, mas também alteraria o URL no navegador para que, se o usuário clicar em recarregar ou marcar, o novo URL seja usado?

Também seria bom se o botão voltar recarregasse o URL original.

Estou tentando registrar o estado do JavaScript no URL.

Steven Noble
fonte
1
Isso seria tão legal. Obviamente, isso seria limitado a modificações no mesmo domínio. Mas algum controle do caminho do lado do cliente (e não apenas o hash) é uma etapa lógica agora que as recargas de páginas são uma espécie de "último recurso" para muitos aplicativos.
Harpo
1
Um "bom tipo de" bom uso de pushState:for(i=1;i<50;i++){var txt="..................................................";txt=txt.slice(0,i)+"HTML5"+txt.slice(i,txt.length);history.pushState({}, "html5", txt);}
Derek #: 07712
exemplo deste efeito em ação: dujour.com
Ben
2
Exemplo deste efeito: facebook.com (Ao abrir imagens na mesa de luz)
Essa é uma boa pergunta. no entanto, é uma situação triste, pois se essa pergunta tivesse sido feita dessa maneira hoje em dia, ela teria sido rebaixada e adotada pelo "este não é o tipo de site em que você leva as pessoas a escreverem todo o seu código para você".
dewd

Respostas:

118

Com HTML 5, utilize a history.pushStatefunção . Como um exemplo:

<script type="text/javascript">
var stateObj = { foo: "bar" };
function change_my_url()
{
   history.pushState(stateObj, "page 2", "bar.html");
}
var link = document.getElementById('click');
link.addEventListener('click', change_my_url, false);
</script>

e um href:

<a href="#" id='click'>Click to change url to bar.html</a>

Se você deseja alterar o URL sem adicionar uma entrada à lista de botões Voltar, use history.replaceState.

clu3
fonte
Você está certo, da última vez que experimentei o Chrome. Eu apenas tentei agora com o Firefox 3.6 no meu Ubuntu, não funcionou. Acho que teremos que esperar até que o HTML5 seja totalmente suportado no FF
clu3
15
O código de exemplo é cortado e colado em developer.mozilla.org/en/DOM/Manipulating_the_browser_history . O que na verdade incomoda explicar o que foo e bar significam nesse caso.
Mikemaccana
2
O foo e a barra não significam nada, é um objeto de estado definido arbitrariamente que você pode voltar mais tarde.
Paul Dixon
3
@ Paul: sim, o artigo acima fornece essas informações.
Mikemaccana
2
Como postar este comentário, ele funciona no Firefox e Safari do Firefox. Sem sorte com safari móvel no iPad ou iPhone embora :(
Sid
187

Se você quer que ele funcione em navegadores que não suportam history.pushStatee history.popStateainda, a forma "antiga" é para definir o identificador de fragmento, que não irá causar um recarregamento da página.

A idéia básica é definir a window.location.hashpropriedade com um valor que contenha todas as informações de estado necessárias e, em seguida, use o evento window.onhashchange ou para navegadores mais antigos que não suportam onhashchange(IE <8, Firefox <3.6), verifique periodicamente para veja se o hash mudou (usando, setIntervalpor exemplo) e atualize a página. Você também precisará verificar o valor do hash no carregamento da página para configurar o conteúdo inicial.

Se você estiver usando o jQuery, há um plug-in de hashchange que utilizará qualquer método que o navegador suporte. Tenho certeza de que existem plugins para outras bibliotecas também.

Uma coisa a ter cuidado é colidir com os IDs na página, porque o navegador irá rolar para qualquer elemento com um ID correspondente.

Matthew Crumley
fonte
2
Os mecanismos de pesquisa não ignoram esses links internos (hashes)? No meu cenário, quero que as aranhas também apanham esses links.
Drew Noakes
3
@Drew, até onde eu sei, não há como os mecanismos de pesquisa tratarem parte de uma página como um resultado separado.
Matthew Crumley
8
Agora existe uma proposta para fazer com que os mecanismos de pesquisa vejam hashes: googlewebmastercentral.blogspot.com/2009/10/…
LKM
5
Em vez de usar setIntervalpara inspecionar, document.location.hashpode-se usar o hashchangeevento, pois é muito mais adotado. Verifique aqui quais navegadores suportam cada padrão . Minha abordagem atual é usar push / popState em navegadores compatíveis. Nos outros, defino o hash e assisto apenas aos hashchangenavegadores de suporte. Isso deixa o IE <= 7 sem suporte para histórico, mas com link permanente útil. Mas quem se importa com a história do IE <= 7?
Irae Carvalho
2
@gabeDel Sim, embora eu ache que o Analytics não registra o hash, ele teria o mesmo URL. Uma maneira melhor seria ligar manualmente _trackPageviewcom o URL "real" da nova página.
Matthew Crumley
37

window.location.href contém o URL atual. Você pode ler a partir dele, anexá-lo e substituí-lo, o que pode causar o recarregamento da página.

Se, como parece, você deseja gravar o estado do javascript no URL para que possa ser marcado, sem recarregar a página, anexá-lo ao URL atual após um # e ter um trecho de javascript acionado pelo evento onload, analisar o atual URL para ver se contém o estado salvo.

Se você usa um? em vez de um #, você forçará o recarregamento da página, mas como você analisará o estado salvo ao carregar, isso pode não ser realmente um problema; e isso fará com que os botões de avançar e voltar também funcionem corretamente.

sombra da Lua
fonte
2
gravação estado javascript na URL é exatamente o que eu estou tentando fazer
Steven Noble
21

Eu suspeitaria fortemente que isso não seja possível, porque seria um problema de segurança incrível, se fosse. Por exemplo, eu poderia criar uma página parecida com uma página de login do banco e fazer com que o URL na barra de endereços parecesse com o banco real !

Talvez, se você explicar por que deseja fazer isso, as pessoas possam sugerir abordagens alternativas ...

[Editar em 2011: desde que escrevi esta resposta em 2008, surgiram mais informações sobre uma técnica HTML5 que permite que o URL seja modificado desde que seja da mesma origem]

Paul Dixon
fonte
6
Um problema não muito grande se as alterações sem recarga forem restritas ao caminho, cadeia de caracteres de consulta e fragmento - ou seja, não à autoridade (domínio e outras).
Ollie Saunders
2
youtube.com/user/SilkyDKwan#p/u/2/gTfqaGYVfMg é um exemplo de que é possível, tente trocar vídeos :)
Spidfire
Você só pode alterar o location.hash (tudo depois do #), como Matthew Chumley observa na resposta aceita.
Paul Dixon
2
Você usa o Facebook? O Facebook altera o URL sem recarregar, usando history.pushState().
Derek朕會功夫
Paul Dixon, você deve notar a caixa de entrada do facebook, quando você clica nos nomes no lado esquerdo, apenas o URL é alterado e o novo bate-papo é carregado na caixa de bate-papo, a página não é atualizada. Também existem muito mais sites sobre WebGL eles fazem o mesmo
Dheeraj Thedijje
8

As configurações de segurança do navegador impedem as pessoas de modificar diretamente o URL exibido. Você pode imaginar as vulnerabilidades de phishing que causariam.

A única maneira confiável de alterar o URL sem alterar as páginas é usar um link ou hash interno. por exemplo: http://site.com/page.html se torna http://site.com/page.html#item1 . Essa técnica é frequentemente usada no hijax (AJAX + preserve history).

Ao fazer isso, usarei apenas links para as ações com o hash como href e, em seguida, adicionarei eventos de clique com jquery que usam o hash solicitado para determinar e delegar a ação.

Espero que isso o coloque no caminho certo.

Jethro Larson
fonte
Desculpe, mas sua resposta não é verdadeira. Sim, você pode alterar o URL.
Derek朕會功夫
1
Sim, você pode usar a API do histórico html5, mas não é um navegador cruzado, embora você possa usar um preenchimento de poli que fará o fallback para os URLs de hash.
Jethro Larson
8

O jQuery possui um ótimo plugin para alterar o URL dos navegadores, chamado jQuery-pusher .

JavaScript pushStatee jQuery podem ser usados ​​juntos, como:

history.pushState(null, null, $(this).attr('href'));

Exemplo:

$('a').click(function (event) {

  // Prevent default click action
  event.preventDefault();     

  // Detect if pushState is available
  if(history.pushState) {
    history.pushState(null, null, $(this).attr('href'));
  }
  return false;
});

Usando apenas JavaScript history.pushState() , que altera o referenciador, que é usado no cabeçalho HTTP para objetos XMLHttpRequest criados após a alteração do estado.

Exemplo:

window.history.pushState("object", "Your New Title", "/new-url");

O método pushState ():

pushState()usa três parâmetros: um objeto de estado, um título (que atualmente é ignorado) e (opcionalmente) um URL. Vamos examinar cada um desses três parâmetros com mais detalhes:

  1. objeto de estado - o objeto de estado é um objeto JavaScript associado à nova entrada de histórico criada por pushState(). Sempre que o usuário navega para o novo estado, um evento popstate é acionado e a propriedade state do evento contém uma cópia do objeto de estado da entrada do histórico.

    O objeto de estado pode ser qualquer coisa que possa ser serializada. Como o Firefox salva objetos de estado no disco do usuário para que possam ser restaurados após o usuário reiniciar o navegador, impomos um limite de tamanho de 640k caracteres na representação serializada de um objeto de estado. Se você passar um objeto de estado cuja representação serializada é maior que isso pushState(), o método lançará uma exceção. Se você precisar de mais espaço do que isso, é recomendável usar o sessionStorage e / ou localStorage.

  2. title - O Firefox atualmente ignora esse parâmetro, embora possa usá-lo no futuro. Passar a string vazia aqui deve ser seguro contra futuras alterações no método. Como alternativa, você pode passar um título curto para o estado para o qual está se mudando.

  3. URL - O URL da nova entrada do histórico é fornecido por esse parâmetro. Observe que o navegador não tentará carregar esse URL após uma chamada pushState(), mas pode tentar carregá-lo mais tarde, por exemplo, depois que o usuário reiniciar o navegador. O novo URL não precisa ser absoluto; se for relativo, é resolvido em relação ao URL atual. O novo URL deve ser da mesma origem que o URL atual; caso contrário, pushState()lançará uma exceção. Este parâmetro é opcional; se não for especificado, será definido como o URL atual do documento.

Ilia Rostovtsev
fonte
3

A galeria de fotos do Facebook faz isso usando um #hash na URL. Aqui estão alguns exemplos de URLs:

Antes de clicar em "próximo":

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507

Depois de clicar em "próximo":

/photo.php?fbid=496429237507&set=a.218088072507.133423.681812507&pid=5887027&id=681812507#!/photo.php?fbid=496435457507&set=a.218088072507.133423.681812507&pid=5887085&id=681812507

Observe o hash-bang (#!) Imediatamente seguido pelo novo URL.

Jonathon Hill
fonte
2

O que está funcionando para mim é - history.replaceState()função que é a seguinte -

history.replaceState(data,"Title of page"[,'url-of-the-page']);

Isso não recarregará a página, você pode usá-la com o evento de javascript

Veshraj Joshi
fonte
1

Eu queria saber se será possível, desde que o caminho pai na página seja o mesmo, apenas algo novo seja anexado a ele.

Então, digamos que o usuário esteja na página: http://domain.com/site/page.html então o navegador pode me deixar fazer location.append = new.html e a página se torna: http://domain.com/site/page.htmlnew.htmle o navegador não a altera.

Ou apenas permita que a pessoa altere o parâmetro get, então vamos location.get = me=1&page=1.

Portanto, a página original se torna http://domain.com/site/page.html?me=1&page=1e não é atualizada.

O problema com # é que os dados não são armazenados em cache (pelo menos acho que não) quando o hash é alterado. Portanto, é como cada vez que uma nova página é carregada, enquanto os botões voltar e avançar em uma página que não seja do Ajax conseguem armazenar dados em cache e não gastam tempo recarregando os dados.

Pelo que vi, o histórico do Yahoo já carrega todos os dados de uma só vez. Parece não estar fazendo nenhuma solicitação do Ajax. Portanto, quando a divé usado para lidar com horas extras de métodos diferentes, esses dados não são armazenados para cada estado do histórico.

user58670
fonte
1

meu código é:

//change address bar
function setLocation(curLoc){
    try {
        history.pushState(null, null, curLoc);
        return false;
    } catch(e) {}
        location.hash = '#' + curLoc;
}

e ação:

setLocation('http://example.com/your-url-here');

e exemplo

$(document).ready(function(){
    $('nav li a').on('click', function(){
        if($(this).hasClass('active')) {

        } else {
            setLocation($(this).attr('href'));
        }
            return false;
    });
});

Isso é tudo :)

Tusko Trush
fonte
1

Uma resposta mais simples que apresento,

window.history.pushState(null, null, "/abc")

isso será adicionado /abcapós o nome do domínio no URL do navegador. Basta copiar esse código e colá-lo no console do navegador e ver o URL mudando para " https://stackoverflow.com/abc "

Sam
fonte
0

Eu tive sucesso com:

location.hash="myValue";

Apenas adiciona #myValueao URL atual. Se você precisar acionar um evento na página Carregar, use o mesmo location.hashpara verificar o valor relevante. Lembre-se de remover #o valor retornado por location.hashex.

var articleId = window.location.hash.replace("#","");
Adam Hey
fonte