Como posso atualizar o window.location.hash sem pular o documento?


163

Eu tenho um painel deslizante configurado no meu site.

Quando terminou de animar, defini o hash da seguinte maneira

function() {
   window.location.hash = id;
}

(trata-se de um retorno de chamada e idé atribuído anteriormente).

Isso funciona bem, para permitir que o usuário adicione o painel aos favoritos e também para que a versão não JavaScript funcione.

No entanto, quando eu atualizo o hash, o navegador pula para o local. Eu acho que esse é um comportamento esperado.

Minha pergunta é: como posso evitar isso? Ou seja, como posso alterar o hash da janela, mas não fazer com que o navegador role para o elemento se o hash existir? Algum tipo event.preventDefault()de coisa?

Estou usando o jQuery 1.4 e o plugin scrollTo .

Muito Obrigado!

Atualizar

Aqui está o código que altera o painel.

$('#something a').click(function(event) {
    event.preventDefault();
    var link = $(this);
    var id = link[0].hash;

    $('#slider').scrollTo(id, 800, {
        onAfter: function() {

            link.parents('li').siblings().removeClass('active');
            link.parent().addClass('active');
            window.location.hash = id;

            }
    });
});

2
Eu suponho que você já tentou event.preventDefault():)
— Marko

@ Marko Eu não sei onde colocá-lo!
— 6288 alex

Você pode postar o restante do seu código?
— Marko

@ Marko Ivanovski Não acho relevante, mas vou ver o que posso fazer.
— 6281 alex

3
@ Gareth Acho que não há lugar para isso, porque acontece assim que eu atualizo o hash.
— 6101 alex

Respostas:


261

Existe uma solução alternativa usando a API de histórico em navegadores modernos com fallback nos antigos:

if(history.pushState) {
    history.pushState(null, null, '#myhash');
}
else {
    location.hash = '#myhash';
}

Crédito vai para Lea Verou


Esta é a melhor resposta, na minha opinião.
— Greg Annandale

10
Observe que o pushState tem o efeito colateral (recuado) de adicionar um estado à pilha de histórico do navegador. Em outras palavras, quando o usuário clicar no botão Voltar, nada acontecerá, a menos que você também adicione um ouvinte de evento popstate.
— David Cook

32
@ DavidCook - Também poderíamos usar history.replaceState(o que, para alterações de hash, pode fazer mais sentido) para evitar a necessidade de um popstateouvinte de evento.
— Jack

Isso funcionou para mim. Eu gosto do efeito da mudança da história. Quero acrescentar a ressalva de que isso não acionará o hashchangeevento. Isso era algo que eu tinha que resolver.
— Jordan

Aviso! isso não vai desencadear um hashchangeevento
— santiago Arizti

52

O problema é que você está configurando window.location.hash para o atributo de ID de um elemento. É o comportamento esperado para o navegador pular para esse elemento, independentemente de você "preventDefault ()" ou não.

Uma maneira de contornar isso é prefixar o hash com um valor arbitrário da seguinte maneira:

window.location.hash = 'panel-' + id.replace('#', '');

Em seguida, tudo o que você precisa fazer é verificar o hash prefixado no carregamento da página. Como um bônus adicional, você pode até rolar suavemente até ele, agora que você está no controle do valor do hash ...

$(function(){
    var h = window.location.hash.replace('panel-', '');
    if (h) {
        $('#slider').scrollTo(h, 800);
    }
});

Se você precisar que isso funcione o tempo todo (e não apenas no carregamento inicial da página), você pode usar uma função para monitorar as alterações no valor do hash e pular para o elemento correto rapidamente:

var foundHash;
setInterval(function() {
    var h = window.location.hash.replace('panel-', '');
    if (h && h !== foundHash) {
        $('#slider').scrollTo(h, 800);
        foundHash = h;
    }
}, 100);

2
Esta é a única solução que realmente responde à pergunta - permitindo hashing sem salto
— amosmos

Isso realmente responde à pergunta que explica a adição e exclusão do valor arbitrário do hash para evitar o salto conforme o comportamento padrão do navegador.
— Lowtechsun 27/05

1
boa solução, mas enfraquece a solução PO, uma vez que nega o uso de seções bookmarking
— Samus

27

Solução barata e desagradável .. Use o feio #! estilo.

Para configurá-lo:

window.location.hash = '#!' + id;

Para ler:

id = window.location.hash.replace(/^#!/, '');

Como ele não corresponde à âncora ou ao ID da página, ele não salta.


3
Eu tive que preservar a compatibilidade com o IE 7 e algumas versões móveis do site. Esta solução foi mais limpa para mim. Geralmente eu estaria em todas as soluções pushState.
— Eric Goodwin

1
definindo o hash com: window.location.hash = '#/' + id;e, em seguida, substituí-lo com: window.location.hash.replace(/^#\//, '#');vai embelezar a url um pouco -> projects/#/tab1
— braitsch

13

Por que você não obtém a posição atual de rolagem, coloque-a em uma variável, atribua o hash e coloque a rolagem da página de volta para onde estava:

var yScroll=document.body.scrollTop;
window.location.hash = id;
document.body.scrollTop=yScroll;

isso deve funcionar


1
hm, este estava trabalhando para mim por algum tempo, mas em algum momento nos últimos meses este parou de funcionar no firefox ....
— matchew

10

Usei uma combinação da solução Attila Fulop (Lea Verou) para navegadores modernos e a solução Gavin Brock para navegadores antigos da seguinte maneira:

if (history.pushState) {
    // IE10, Firefox, Chrome, etc.
    window.history.pushState(null, null, '#' + id);
} else {
    // IE9, IE8, etc
    window.location.hash = '#!' + id;
}

Conforme observado por Gavin Brock, para capturar o id de volta, você terá que tratar a string (que neste caso pode ter ou não o "!") Da seguinte maneira:

id = window.location.hash.replace(/^#!?/, '');

Antes disso, tentei uma solução semelhante à proposta pelo user706270, mas não funcionou bem com o Internet Explorer: como o mecanismo Javascript não é muito rápido, você pode observar o aumento e a diminuição da rolagem, o que produz um efeito visual desagradável.


2

Esta solução funcionou para mim.

O problema com a configuração location.hashé que a página passará para esse ID se for encontrada na página.

O problema window.history.pushStateé que ele adiciona uma entrada ao histórico para cada guia em que o usuário clica. Então, quando o usuário clica no backbotão, eles vão para a guia anterior. (isso pode ou não ser o que você deseja. não era o que eu queria).

Para mim, replaceStatefoi a melhor opção, pois substitui apenas o histórico atual; portanto, quando o usuário clica no backbotão, eles vão para a página anterior.

$('#tab-selector').tabs({
  activate: function(e, ui) {
    window.history.replaceState(null, null, ui.newPanel.selector);
  }
});

Confira os documentos da API do histórico no MDN.


1

Não tenho certeza se você pode alterar o elemento original, mas que tal mudar de usar o id attr para algo mais como data-id? Em seguida, basta ler o valor do ID de dados para o seu valor de hash e ele não irá pular.


1

Esta solução funcionou para mim

// store the currently selected tab in the hash value
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
$('#myTab a[href="' + hash + '"]').tab('show');

E meu código js completo é

$('#myTab a').click(function(e) {
  e.preventDefault();
  $(this).tab('show');
});

// store the currently selected tab in the hash value
$("ul.nav-tabs > li > a").on("shown.bs.tab", function(e) {
  var id = $(e.target).attr("href").substr(1);
    if(history.pushState) {
        window.history.pushState(null, null, '#' + id);
    }
    else {
        window.location.hash = id;
    }
   // window.location.hash = '#!' + id;
});

// on load of the page: switch to the currently selected tab
var hash = window.location.hash;
// console.log(hash);
$('#myTab a[href="' + hash + '"]').tab('show');

0

Ao usar o laravel framework, tive alguns problemas com o uso de uma função route-> back (), pois ela apagou meu hash. Para manter meu hash, criei uma função simples:

$(function() {   
    if (localStorage.getItem("hash")    ){
     location.hash = localStorage.getItem("hash");
    }
}); 

e defini-o na minha outra função JS como esta:

localStorage.setItem("hash", myvalue);

Você pode nomear seus valores de armazenamento local da maneira que desejar; meu chamado hash.

Portanto, se o hash estiver definido na PAGE1 e você navegar para PAGE2; o hash será recriado na PAGE1 quando você clicar em Voltar na PAGE2.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.