Como desativar o “salto” da âncora ao carregar uma página?

111

Acho que isso pode não ser possível, vou tentar explicar da melhor maneira que puder. Eu tenho uma página contendo guias (jquery powered), controlada pelo seguinte:

Estou usando este código, fornecido por outro usuário em uma pergunta anterior.

<script type="text/javascript">
    $(function() {

     $('html, body').animate({scrollTop:0}); // this is my "fix"

        var tabContent = $(".tab_content");
        var tabs = $("#menu li");
        var hash = window.location.hash;
     tabContent.not(hash).hide();
        if(hash=="") {
      $('#tab1').fadeIn();
     }
        tabs.find('[href=' + hash + ']').parent().addClass('active');

        tabs.click(function() {
            $(this).addClass('active').siblings().removeClass('active');
            tabContent.hide();
            var activeTab = $(this).find("a").attr("href");

            $(activeTab).fadeIn();
           return false;
        });

    });
</script>

esse código funciona muito bem quando eu visito a página "guias" diretamente.

no entanto, preciso criar um link para guias individuais de outras páginas - então, para fazer isso, o código obtém o window.location.hashe mostra a guia apropriada.

a página não "pula" para a âncora por causa do "retorno falso".

este evento só é acionado em um evento de clique. portanto, se eu visitar minhas "guias" em qualquer outra página, o efeito de "pulo" é ativado. Para combater isso, vou automaticamente até o topo da página, mas prefiro que isso não aconteça.

existe alguma maneira de simular o "retorno falso" quando a página carrega, evitando que o "salto" da âncora ocorra.

espero que isso esteja claro o suficiente.

obrigado

Ross
fonte

Respostas:

143

Sua correção não funciona? Não tenho certeza se entendi a pergunta corretamente - você tem uma página de demonstração? Você poderia tentar:

if (location.hash) {
  setTimeout(function() {

    window.scrollTo(0, 0);
  }, 1);
}

Editar: testado e funciona no Firefox, IE e Chrome no Windows.
Editar 2: mover setTimeout()dentro do ifbloco, props @vsync.

dave1010
fonte
2
herói! Sim, minha "correção" funcionou, mas a página "rolou" (como meu código disse) e eu preferia que ela não rolasse. seu código funciona perfeitamente. Eu não tinha olhado para ScrollTo - a função é mesmo necessária? parece funcionar bem apenas com window.scrollTo (0, 0);
Ross,
3
Eu tentei no Firefox em uma página vazia sem o setTimeoute nem sempre funcionou. Você provavelmente não precisa disso se estiver no jQuery de $(function() {qualquer maneira. Você realmente não precisa do location.hash, mas é bom deixá-lo ativado para que os navegadores não tenham que fazer nada. Também lembra para que serve o scrollTo!
dave1010
2
Hoje eu tive esse mesmo problema. Na verdade, eu tive que adicionar o hash (idêntico aos nomes de guia / valores href) para meus links de navegação. Acabei adicionando um sufixo de 1 caractere às minhas guias e, em seguida, atribuindo substring aos valores por 1 caractere (str.slice (0, -1) ao compará-los a window.location.hash. Dessa forma, os hashes são diferentes e não ocorrem saltos .
desenvolvedor10
1
O ifdeve estar fora, não dentro. além disso, o mínimo para o intervalo é cerca de 10, então 0 ou 1 é o mesmo ... como 10. dependendo do navegador.
vsync
2
Isso NÃO impede que o navegador pule para a hashtag, você deve colocar uma hashtag vazia para evitar o 'jumpieness', veja minha resposta aqui stackoverflow.com/a/29823913/826194
Larzan
43

Veja minha resposta aqui: Resposta Stackoverflow

O truque é apenas remover a hashtag o mais rápido possível e armazenar seu valor para seu próprio uso:

É importante que você não coloque essa parte do código nas funções $ () ou $ (window) .load (), pois seria tarde demais e o navegador já teria passado para a tag.

// store the hash (DON'T put this code inside the $() function, it has to be executed 
// right away before the browser can start scrolling!
var target = window.location.hash,
    target = target.replace('#', '');

// delete hash so the page won't scroll to it
window.location.hash = "";

// now whenever you are ready do whatever you want
// (in this case I use jQuery to scroll to the tag after the page has loaded)
$(window).on('load', function() {
    if (target) {
        $('html, body').animate({
            scrollTop: $("#" + target).offset().top
        }, 700, 'swing', function () {});
    }
});
Larzan
fonte
1
Eu encontrei um bug com FF onde eu estava usando max-height overflow-y em um div com uma lista muito longa, FF rolaria para baixo onde o nó oculto estaria no dom. Isso usou apenas as três linhas principais do código real (não os comentários) e corrigiu meu problema com o FireFox.
JQII
12

Existem outras maneiras de rastrear em qual guia você está; talvez definindo um cookie ou um valor em um campo oculto, etc. etc.

Eu diria que se você não quiser que a página salte no carregamento, seria melhor usar uma dessas outras opções em vez do hash, porque a principal razão para usar o hash em vez deles é permitir exatamente o que você está querendo bloquear.

Outro ponto - a página não saltará se seus links hash não corresponderem aos nomes das tags no documento, então talvez se você quiser continuar usando o hash, você pode manipular o conteúdo para que as tags tenham nomes diferentes. Se você usar um prefixo consistente, ainda poderá usar Javascript para alternar entre eles.

Espero que ajude.

Spudley
fonte
2
Bons pontos. Não se esqueça de tentar manter a página utilizável / acessível com JS / CSS desativado.
dave1010
12

Usei a solução dave1010, mas ficou um pouco assustada quando a coloquei dentro da função $ (). Ready. Então eu fiz isso: (não dentro de $ (). Pronto)

    if (location.hash) {               // do the test straight away
        window.scrollTo(0, 0);         // execute it straight away
        setTimeout(function() {
            window.scrollTo(0, 0);     // run it a bit later also for browser compatibility
        }, 1);
    }
assimétrico
fonte
10
  1. O navegador pula para <element id="abc" />se houver http://site.com/#abc hash no endereço e o elemento com id = "abc" estiver visível. Então, você só deve ocultar o elemento por padrão: <element id="anchor" style="display: none" />. Se não houver nenhum elemento visível com id = hash na página, o navegador não saltará.

  2. Crie um script que verifique o hash no url e, em seguida, encontre e mostre o elemento apropriado (que está oculto por padrão).

  3. Desative o comportamento padrão de "link semelhante a hash" vinculando um evento onclick a um link e especifique return false;como resultado do evento onclick.

Quando implementei guias, escrevi algo assim:

    <script>
    $(function () {         
        if (location.hash != "") {
            selectPersonalTab(location.hash);
        }
        else {
            selectPersonalTab("#cards");
        }
    });

    function selectPersonalTab(hash) {
        $("#personal li").removeClass("active");
        $("a[href$=" + hash + "]").closest("li").addClass("active");
        $("#personaldata > div").hide();
        location.hash = hash;
        $(hash).show();
    }

    $("#personal li a").click(function (e) {
        var t = e.target;
        if (t.href.indexOf("#") != -1) {
            var hash = t.href.substr(t.href.indexOf("#"));
            selectPersonalTab(hash);
            return false;
        }
    });
</script>
Sergei Smirnov
fonte
6

Nenhuma das respostas não funciona bem para mim, vejo a página pulando para a âncora e depois para o topo para algumas soluções, algumas respostas não funcionam de todo, podem ser coisas mudadas por anos. Espero que minha função ajude alguém.

/**
 * Prevent automatic scrolling of page to anchor by browser after loading of page.
 * Do not call this function in $(...) or $(window).on('load', ...),
 * it should be called earlier, as soon as possible.
 */
function preventAnchorScroll() {
    var scrollToTop = function () {
        $(window).scrollTop(0);
    };
    if (window.location.hash) {
        // handler is executed at most once
        $(window).one('scroll', scrollToTop);
    }

    // make sure to release scroll 1 second after document readiness
    // to avoid negative UX
    $(function () {
        setTimeout(
            function () {
                $(window).off('scroll', scrollToTop);
            },
            1000
        );
    });
}
kdmitry
fonte
A melhor solução que encontrei até agora. E realmente vi muito.
MarkSkayff de
Obrigado por isso! Também a única solução que funcionou para mim depois de horas de tentativas, mas a melhor parte dessa solução para mim é que eu não tenho que remover o hash.
Chris Vecchio
Isso não funciona se o scroller já estiver no topo da página no carregamento da página, então ele rolará para baixo como de costume e depois que a página for concluída, ele pula para o topo. Alguém sabe como é esse o caso?
robgha01
@ robgha01 certifique-se de que a função seja executada o mais rápido possível e não espere por nenhum evento, isso está errado:, $(document).ready(function () { preventAnchorScroll(); });basta chamar a função bem no início do seu JavaScript. Eu testei agora, funciona para mim no Chrome, Firefox e Opera.
kdmitry
4

CSS sujo apenas corrige para permanecer rolado para cima toda vez que a âncora é usada (não é útil se você ainda deseja usar âncoras para saltos de rolagem, muito útil para links diretos):

.elementsWithAnchorIds::before {
   content: "";
   display: block;
   height: 9999px;
   margin-top: -9999px; //higher thin page height
}
outro você
fonte
tãããão inteligente!
duhaime
3

Embora a resposta aceita funcione bem, eu descobri que às vezes, especialmente em páginas que contêm imagens grandes que a barra de rolagem pula rapidamente usando

 window.scrollTo(0, 0);

O que pode ser bastante perturbador para o usuário.

A solução que decidi no final é realmente muito simples, e isso é usar um ID diferente no alvo para aquele usado em location.hash

Por exemplo:

Aqui está o link em alguma outra página

<a href="/page/with/tabs#tab2">Some info found in tab2 on tabs page</a>

Então, é claro, se houver um elemento com um ID de tab2 na página de guias, a janela saltará para ele no carregamento.

Portanto, você pode anexar algo ao ID para evitá-lo:

<div id="tab2-noScroll">tab2 content</div>

E então você pode acrescentar "-noScroll"a location.hashno javascript:

<script type="text/javascript">
    $(function() {

        var tabContent = $(".tab_content");
        var tabs = $("#menu li");
        var hash = window.location.hash;


     tabContent.not(hash + '-noScroll').hide();                           
        if(hash=="") {       //^ here
      $('#tab1-noScroll').fadeIn();
     }
        tabs.find('[href=' + hash + ']').parent().addClass('active');

        tabs.click(function() {
            $(this).addClass('active').siblings().removeClass('active');
            tabContent.hide();
            var activeTab = $(this).find("a").attr("href") + '-noScroll'; 
                                                               //^ and here

            $(activeTab).fadeIn();
           return false;
        });

    });
</script>
Andrew
fonte
2

No meu caso, devido ao uso de link âncora para CSS pop-up, usei desta forma:

Basta salvar o pageYOffset primeiro ao clicar e, em seguida, definir o ScrollTop para:

$(document).on('click','yourtarget',function(){
        var st=window.pageYOffset;
        $('html, body').animate({
                'scrollTop' : st
            });
});
Metro Polisue
fonte
Depois de tentar as outras soluções, esta foi a que funcionou para mim, no meu caso o site com o qual estava trabalhando usava o # na url para carregar uma seção específica de um checkout. Em vez de modificar a funcionalidade de checkout do hash no url, acabei de usar isso. Obrigado
chris c
1

Acho que encontrei uma maneira de usar as guias da interface do usuário sem refazer a funcionalidade. Até agora não encontrei nenhum problema.

    $( ".tabs" ).tabs();
    $( ".tabs" ).not(".noHash").bind("tabsshow", function(event, ui) { 
        window.location.hash = "tab_"+ui.tab.hash.replace(/#/,"");
        return false;
    });

    var selectedTabHash = window.location.hash.replace(/tab_/,"");
    var index = $( ".tabs li a" ).index($(".tabs li a[href='"+selectedTabHash+"']"));
    $( ".tabs" ).not(".noHash").tabs('select', index);

Tudo dentro de um evento pronto. Ele precede "tab_" para o hash, mas funciona tanto quando clicado quanto na página carregada com hash.

JRomero
fonte
1

Isso funcionará com bootstrap v3

$(document).ready(function() {
  if ($('.nav-tabs').length) {
    var hash = window.location.hash;
    var hashEl = $('ul.nav a[href="' + hash + '"]');
    hash && hashEl.tab('show');

    $('.nav-tabs a').click(function(e) {
      e.preventDefault();
      $(this).tab('show');
      window.location.hash = this.hash;
    });

    // Change tab on hashchange
    window.addEventListener('hashchange', function() {
      var changedHash = window.location.hash;
      changedHash && $('ul.nav a[href="' + changedHash + '"]').tab('show');
    }, false);
  }
});
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
<div class="container">
<ul class="nav nav-tabs">
  <li class="active"><a data-toggle="tab" href="#home">Home</a></li>
  <li><a data-toggle="tab" href="#menu1">Menu 1</a></li>
</ul>

<div class="tab-content">
  <div id="home" class="tab-pane fade in active">
    <h3>HOME</h3>
    <p>Some content.</p>
  </div>
  <div id="menu1" class="tab-pane fade">
    <h3>Menu 1</h3>
    <p>Some content in menu 1.</p>
  </div>
</div>
</div>

Lasithds
fonte
0

Outra abordagem

Experimente verificar se a página foi rolada e só então redefina a posição:

var scrolled = false;

$(window).scroll(function(){
  scrolled = true;
});

if ( window.location.hash && scrolled ) {
  $(window).scrollTop( 0 );
}

Heres a demo

hitautodestruct
fonte
não está funcionando mano ... a carga inicial está na parte inferior ... após a atualização permanece na parte superior
Martin Zvarík
0

Não tive muito sucesso com os setTimeoutmétodos acima no Firefox.

Em vez de setTimeout, usei uma função onload:

window.onload = function () {
    if (location.hash) {
        window.scrollTo(0, 0);
    }
};

Ainda é muito problemático, infelizmente.

Bozdoz
fonte
porque o onload é executado depois que tudo está totalmente carregado, incluindo fotos ... então é claro que é problemático desta forma
Martin Zvarík
0

Não tive nenhum sucesso consistente com essas soluções.

Aparentemente devido ao fato de que o salto acontece antes de Javascript => referência ( http://forum.jquery.com/topic/preventing-anchor-jump )

Minha solução é posicionar todas as âncoras no topo por padrão com CSS.

.scroll-target{position:fixed;top:0;left:0;}

Em seguida, use jquery para rolar até o pai da âncora de destino ou um irmão (talvez uma tag em vazia)

<a class="scroll-target" name="section3"></a><em>&nbsp</em>

jquery exemplo para rolar para quando o URL é inserido com hash
(a página não deve estar carregada na janela / guia, isso cobre links de outras fontes externas)

setTimeout(function() {
    if (window.location.hash) {               
        var hash = window.location.hash.substr(1);   
        var scrollPos = $('.scroll-target[name="'+hash+'"]').siblings('em').offset().top; 
        $("html, body").animate({ scrollTop: scrollPos }, 1000);    
    }
}, 1);

Além disso, você deseja evitar o padrão para cliques de âncora enquanto na página e, em seguida, rolar para seus destinos

<a class="scroll-to" href="#section3">section three</a>

e jquery

$('a.scroll-to').click(function(){
    var target = $(this).attr('href').substr(1);
    var scrollPos = $('.scroll-target[name="'+target+'"]').siblings('em').offset().top; 
    $("html, body").animate({ scrollTop: scrollPos }, 1000);
    return false;
});

A vantagem desse método é que os alvos da tag âncora permanecem estruturalmente ao lado do conteúdo relevante, embora sua posição CSS esteja no topo.

Isso deve significar que os rastreadores de mecanismos de pesquisa não terão problemas.

Saúde, espero que isso ajude
Gray

grayskale.com
fonte
0

Tente isso para evitar que o cliente faça qualquer tipo de rolagem vertical em hashchanged (dentro de seu manipulador de eventos):

var sct = document.body.scrollTop;
document.location.hash = '#next'; // or other manipulation
document.body.scrollTop = sct;

(redesenho do navegador)

Andy
fonte
0

Resolvi meu problema fazendo isto:

// navbar height 
var navHeigth = $('nav.navbar').height();    

// Scroll to anchor function
var scrollToAnchor = function(hash) {
  // If got a hash
  if (hash) {
    // Scroll to the top (prevention for Chrome)
    window.scrollTo(0, 0);
    // Anchor element
    var term = $(hash);

    // If element with hash id is defined
    if (term) {

      // Get top offset, including header height
      var scrollto = term.offset().top - navHeigth;

      // Capture id value
      var id = term.attr('id');
      // Capture name value
      var name = term.attr('name');

      // Remove attributes for FF scroll prevention
      term.removeAttr('id').removeAttr('name');
      // Scroll to element
      $('html, body').animate({scrollTop:scrollto}, 0);

      // Returning id and name after .5sec for the next scroll
      setTimeout(function() {
        term.attr('id', id).attr('name', name);
      }, 500);
    }
  }
};

// if we are opening the page by url
if (location.hash) {
  scrollToAnchor(location.hash);
}

// preventing default click on links with an anchor
$('a[href*="#"]').click(function(e) {
  e.preventDefault();
  // hash value
  var hash = this.href.substr(this.href.indexOf("#"));
  scrollToAnchor(hash);
});`
xottabych007
fonte