Come disabilitare il "salto" di ancoraggio durante il caricamento di una pagina?


111

Penso che questo potrebbe non essere possibile, cercherò di spiegare nel miglior modo possibile. Ho una pagina contenente schede (alimentata da jquery), controllata da quanto segue:

Sto usando questo codice, fornito da un altro utente da una domanda precedente.

<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>

questo codice funziona alla grande quando visito direttamente la pagina "schede".

tuttavia, ho bisogno di collegarmi a schede individuali da altre pagine, quindi per fare ciò, il codice ottiene il window.location.hashquindi mostra la scheda appropriata.

la pagina non "salta" all'ancora a causa di "return false".

tuttavia, questo evento viene attivato solo su un evento clic. quindi, se visito le mie "schede" da qualsiasi altra pagina, viene attivato l'effetto "salto". Per evitare questo, scorro automaticamente fino all'inizio della pagina, ma preferirei che non accadesse.

c'è un modo per simulare "return false" quando la pagina viene caricata, impedendo che si verifichi il "jump" di ancoraggio.

spero che questo sia abbastanza chiaro.

Grazie

Risposte:


143

La tua correzione non funziona? Non sono sicuro di aver capito correttamente la domanda: hai una pagina demo? Potresti provare:

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

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

Modifica: testato e funziona in Firefox, IE e Chrome su Windows.
Modifica 2: sposta setTimeout()all'interno del ifblocco, props @vsync.


2
eroe! Sì, la mia "correzione" ha funzionato, ma la pagina "scorreva" (come le diceva il mio codice), e avrei preferito che non scorresse. il tuo codice funziona perfettamente. Non avevo esaminato ScrollTo: la funzione è anche necessaria? sembra funzionare bene solo con window.scrollTo (0, 0);
Ross

3
L'ho provato in Firefox su una pagina vuota senza setTimeoute non sempre ha funzionato. Probabilmente non ne hai bisogno se è $(function() {comunque in jQuery . Non hai davvero bisogno di location.hash, ma è bello lasciarlo acceso in modo che i browser non debbano fare nulla. Ti ricorda anche a cosa serve lo scrollTo!
dave1010

2
Oggi ho avuto lo stesso problema. In realtà ho dovuto aggiungere l'hash (identico ai nomi delle schede / valori href) ai miei collegamenti di navigazione. Ho finito per aggiungere un suffisso di 1 carattere alle mie schede, quindi sottostringa i valori di 1 carattere (str.slice (0, -1) quando li confrontavo con window.location.hash In questo modo gli hash sono diversi e non si verificano salti .
developer10

1
Il ifdovrebbe essere al di fuori, non dentro. inoltre, il minimo per l'intervallo è di circa 10, quindi 0 o 1 è uguale .. come 10. a seconda del browser.
vsync

2
Ciò non impedisce al browser di saltare l'hashtag, bisogna mettere un hashtag vuoto per evitare che il 'jumpieness', vedi la mia risposta qui stackoverflow.com/a/29823913/826194
Larzan

43

Vedi la mia risposta qui: Stackoverflow Answer

Il trucco sta nel rimuovere appena possibile l'hashtag e memorizzarne il valore per uso personale:

È importante non inserire quella parte di codice nelle funzioni $ () o $ (window) .load () poiché sarebbe troppo tardi e il browser si è già spostato nel 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 () {});
    }
});

1
mi ero imbattuto in un bug con FF in cui stavo usando max-height overflow-y su un div con un elenco molto lungo, FF avrebbe fatto scorrere verso il basso dove sarebbe stato il nodo nascosto nel dom. Questo utilizzava solo le prime tre righe di codice effettivo (non i commenti) e questo ha corretto il mio problema con FireFox.
JQII

12

Esistono altri modi per monitorare la scheda in cui ti trovi; magari impostando un cookie, o un valore in un campo nascosto, ecc ecc.

Direi che se non vuoi che la pagina salti al caricamento, faresti meglio a usare una di queste altre opzioni piuttosto che l'hash, perché il motivo principale per usare l'hash in preferenza è per consentire esattamente quello che tu vuoi bloccare.

Un altro punto: la pagina non salterà se i tuoi link hash non corrispondono ai nomi dei tag nel documento, quindi forse se vuoi continuare a usare l'hash potresti manipolare il contenuto in modo che i tag abbiano un nome diverso. Se utilizzi un prefisso coerente, sarai comunque in grado di utilizzare Javascript per passare da allora.

Spero che aiuti.


2
Punti buoni. Non dimenticare di provare a mantenere la pagina utilizzabile / accessibile con JS / CSS disattivato.
dave1010

12

Ho usato la soluzione di dave1010, ma è stato un po 'nervoso quando l'ho inserita nella funzione $ (). Ready. Quindi ho fatto questo: (non all'interno di $ (). Ready)

    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);
    }

10
  1. Il browser passa a <element id="abc" />se sono presenti hash http://site.com/#abc nell'indirizzo e l'elemento con id = "abc" è visibile. Quindi, basta dovrebbe nascondere l'elemento per impostazione predefinita: <element id="anchor" style="display: none" />. Se non ci sono elementi visibili con id = hash nella pagina, il browser non salterà.

  2. Crea uno script che controlli l'hash nell'url, quindi trova e mostra l'elemento prrpopriate (che è nascosto per impostazione predefinita).

  3. Disabilita il comportamento predefinito "collegamento tipo hash" associando un evento onclick a un collegamento e specificalo return false;come risultato dell'evento onclick.

Quando ho implementato le schede, ho scritto qualcosa del genere:

    <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>

6

Nessuna delle risposte non funziona abbastanza bene per me, vedo la pagina che salta per ancorare e poi in cima per alcune soluzioni, alcune risposte non funzionano affatto, le cose potrebbero essere cambiate per anni. Spero che la mia funzione possa aiutare qualcuno.

/**
 * 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
        );
    });
}

La migliore soluzione che ho trovato finora. E in realtà ho visto molto.
MarkSkayff

Grazie per questo! Anche l'unica soluzione che ha funzionato per me dopo ore di tentativi, ma la parte migliore di questa soluzione per me è che non devo rimuovere l'hash.
Chris Vecchio

Questo non funziona se lo scroller è già nella parte superiore della pagina al caricamento della pagina, quindi scorrerà verso il basso come al solito e dopo che la pagina è terminata salta all'inizio. Qualcuno sa come stanno le cose?
robgha01

@ robgha01 per favore assicurati che la funzione sia eseguita il prima possibile e non aspetti alcun evento, questo è sbagliato:, $(document).ready(function () { preventAnchorScroll(); });chiama la funzione all'inizio del tuo JavaScript. L'ho provato ora, funziona per me in Chrome, Firefox e Opera.
kdmitry,

4

Il CSS sporco risolve solo lo scorrimento verso l'alto ogni volta che viene utilizzato l'ancoraggio (non utile se si desidera comunque utilizzare gli ancoraggi per i salti di scorrimento, molto utile per il collegamento profondo):

.elementsWithAnchorIds::before {
   content: "";
   display: block;
   height: 9999px;
   margin-top: -9999px; //higher thin page height
}

mooooolto intelligente!
duhaime

3

Anche se la risposta accettata funziona bene, ho scoperto che a volte, specialmente su pagine contenenti immagini di grandi dimensioni, la barra di scorrimento salterà selvaggiamente usando

 window.scrollTo(0, 0);

Il che può essere abbastanza fonte di distrazione per l'utente.

La soluzione su cui ho deciso alla fine è in realtà piuttosto semplice, ed è quella di utilizzare un ID diverso sul target da quello utilizzato in location.hash

Per esempio:

Ecco il link in qualche altra pagina

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

Quindi, ovviamente, se c'è un elemento con un ID tab2nella pagina delle schede, la finestra salterà su di esso al caricamento.

Quindi puoi aggiungere qualcosa all'ID per impedirlo:

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

E poi si può accodare "-noScroll"a location.hashnella 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>

2

Nel mio caso, a causa dell'utilizzo del link di ancoraggio per il popup CSS, ho usato in questo modo:

Basta salvare la pagina YOffset per prima cosa al clic e quindi impostare ScrollTop su questo:

$(document).on('click','yourtarget',function(){
        var st=window.pageYOffset;
        $('html, body').animate({
                'scrollTop' : st
            });
});

Dopo aver provato le altre soluzioni, questa è stata quella che ha funzionato per me, nel mio caso il sito Web con cui stavo lavorando ha utilizzato il # nell'URL per caricare una sezione specifica di un checkout. Piuttosto che modificare la funzionalità di checkout per l'hash nell'URL, l'ho appena usato. Grazie
chris c

1

Penso di aver trovato un modo per le schede dell'interfaccia utente senza dover ripetere la funzionalità. Finora non ho riscontrato alcun 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);

Tutto in un evento pronto. Antepone "tab_" per l'hash ma funziona sia quando si fa clic che nella pagina caricata con hash.


1

Funzionerà con 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>


0

Un altro approccio

Prova a controllare se la pagina è stata fatta scorrere e solo dopo ripristina la posizione:

var scrolled = false;

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

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

Heres a demo


non funziona fratello ... il caricamento iniziale è in basso ... dopo l'aggiornamento rimane in alto
Martin Zvarík

0

Non ho avuto molto successo con i setTimeoutmetodi sopra in Firefox.

Invece di un setTimeout, ho usato una funzione onload:

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

È ancora molto glitch, sfortunatamente.


perché onload viene eseguito dopo che tutto è stato caricato completamente, comprese le foto ... quindi ovviamente è glitch in questo modo
Martin Zvarík

0

Non ho avuto alcun successo costante con queste soluzioni.

Apparentemente a causa del fatto che il salto avviene prima di Javascript => reference ( http://forum.jquery.com/topic/preventing-anchor-jump )

La mia soluzione è posizionare tutti gli ancoraggi in alto per impostazione predefinita con CSS.

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

Quindi usa jquery per scorrere fino al genitore dell'ancora di destinazione o un fratello (forse un tag em vuoto)

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

Esempio di jquery per lo scorrimento per quando l'URL viene inserito con un hash
(la pagina non deve essere già caricata nella finestra / scheda, questo copre i collegamenti da altre fonti esterne)

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);

Inoltre ti consigliamo di impedire l'impostazione predefinita per i clic di ancoraggio mentre sei sulla pagina e quindi scorrere fino ai loro obiettivi

<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;
});

La cosa buona di questo metodo è che i target dei tag di ancoraggio rimangono strutturalmente accanto al contenuto pertinente, sebbene la loro posizione CSS sia in alto.

Ciò dovrebbe significare che i crawler dei motori di ricerca non avranno problemi.

Salute, spero che questo aiuti
Gray


0

Prova questo per impedire al client di qualsiasi tipo di scorrimento verticale su hashchanged (all'interno del tuo gestore di eventi):

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

(ridisegno del browser)


0

Ho risolto il mio problema in questo modo:

// 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);
});`
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.