Jak wyłączyć „skok” kotwicy podczas ładowania strony?

111

Myślę, że może to nie być możliwe, postaram się wyjaśnić najlepiej, jak potrafię. Mam stronę zawierającą zakładki (obsługiwane przez jQuery), kontrolowane przez:

Używam tego kodu podanego przez innego użytkownika z poprzedniego pytania.

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

ten kod działa świetnie, gdy bezpośrednio odwiedzam stronę „karty”.

jednak muszę linkować do poszczególnych zakładek z innych stron - aby to zrobić, kod pobiera window.location.hashi wyświetla odpowiednią zakładkę.

strona nie „przeskakuje” do kotwicy z powodu „return false”.

to zdarzenie jest jednak wyzwalane tylko w przypadku kliknięcia. dlatego też, jeśli odwiedzę moje „zakładki” z dowolnej innej strony, wywoływany jest efekt „skoku”. Aby temu przeciwdziałać, automatycznie przewijam na górę strony, ale wolałbym, żeby tak się nie stało.

czy istnieje sposób, aby zasymulować „return false” podczas ładowania strony, zapobiegając wystąpieniu „skoku” kotwicy.

mam nadzieję, że to jest wystarczająco jasne.

dzięki

Ross
źródło

Odpowiedzi:

143

Twoja poprawka nie działa? Nie jestem pewien, czy dobrze rozumiem pytanie - czy masz stronę demonstracyjną? Możesz spróbować:

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

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

Edycja: przetestowana i działa w przeglądarkach Firefox, IE i Chrome w systemie Windows.
Edycja 2: poruszanie się setTimeout()wewnątrz ifbloku, props @vsync.

dave1010
źródło
2
bohater! Tak, moja „poprawka” zadziałała, ale strona „przewijała się” (tak jak nakazał mój kod), a wolałbym, żeby się nie przewijała. Twój kod działa doskonale. Nie patrzyłem na ScrollTo - czy ta funkcja jest w ogóle konieczna? wygląda na to, że działa dobrze tylko z window.scrollTo (0, 0);
Ross
3
Wypróbowałem to w Firefoksie na pustej stronie bez znaku setTimeouti nie zawsze działało. Prawdopodobnie nie potrzebujesz tego, jeśli i tak jest w jQuery $(function() {. Tak naprawdę nie potrzebujesz location.hash, ale fajnie jest zostawić go w tym miejscu, więc przeglądarki mogą nie musieć nic robić. Przypomina również, do czego służy scrollTo!
dave1010,
2
Dzisiaj miałem ten sam problem. Właściwie musiałem dodać hash (identyczny z nazwami kart / wartościami href) do moich linków nawigacyjnych. Skończyło się na dodaniu sufiksu 1-znakowego do moich zakładek, a następnie podciąganiu wartości o 1 znak (str.slice (0, -1) podczas porównywania ich z window.location.hash W ten sposób skróty są różne i nie występują przeskoki .
developer10
1
ifPowinny być na zewnątrz, a nie wewnątrz. również minimalny interwał wynosi około 10, więc 0 lub 1 to to samo… jako 10. w zależności od przeglądarki.
vsync,
2
NIE zapobiega to przechodzeniu przeglądarki do hashtagu, musisz wstawić pusty hashtag, aby zapobiec `` skoczności '', zobacz moją odpowiedź tutaj stackoverflow.com/a/29823913/826194
Larzan
43

Zobacz moją odpowiedź tutaj: Odpowiedź Stackoverflow

Sztuczka polega na tym, aby jak najszybciej usunąć hashtag i zachować jego wartość do własnego użytku:

Ważne jest, aby nie umieszczać tej części kodu w funkcjach $ () lub $ (window) .load (), ponieważ byłoby za późno, a przeglądarka została już przeniesiona do znacznika.

// 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
źródło
1
Napotkałem błąd w FF, w którym używałem max-height overflow-y w div z naprawdę długą listą, FF przewinąłby w dół, gdzie ukryty węzeł byłby w dom. To wykorzystywało tylko trzy pierwsze wiersze rzeczywistego kodu (nie komentarze), co rozwiązało mój problem z FireFox.
JQII
12

Istnieją inne sposoby śledzenia, na której karcie się znajdujesz; być może ustawienie pliku cookie lub wartości w ukrytym polu itp.

Powiedziałbym, że jeśli nie chcesz, aby strona podskakiwała podczas ładowania, lepiej byłoby użyć jednej z tych innych opcji zamiast mieszania, ponieważ głównym powodem używania skrótu zamiast nich jest zezwolenie na dokładnie to, co chcesz chcą się zablokować.

Kolejna kwestia - strona nie będzie przeskakiwać, jeśli twoje linki z skrótem nie będą pasować do nazw tagów w dokumencie, więc być może jeśli chcesz nadal używać skrótu, możesz manipulować zawartością, aby tagi były nazwane inaczej. Jeśli użyjesz spójnego prefiksu, nadal będziesz mógł używać Javascript do przechodzenia między nimi.

Mam nadzieję, że to pomoże.

Spudley
źródło
2
Słuszne uwagi. Nie zapomnij, aby strona była użyteczna / dostępna po wyłączeniu JS / CSS.
dave1010,
12

Użyłem rozwiązania dave1010, ale było trochę niepewne, kiedy wstawiłem je do funkcji $ (). Ready. Więc zrobiłem to: (nie wewnątrz $ (). Gotowe)

    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);
    }
przechylony
źródło
10
  1. Przeglądarka <element id="abc" />przechodzi do, jeśli adres zawiera hash http://site.com/#abc, a element o identyfikatorze id = "abc" jest widoczny. Więc, po prostu należy ukryć element domyślnie: <element id="anchor" style="display: none" />. Jeśli na stronie nie ma widocznego elementu z id = hash, przeglądarka nie przeskoczy.

  2. Utwórz skrypt, który sprawdza hash w adresie URL, a następnie znajduje i wyświetla odpowiedni element (który jest domyślnie ukryty).

  3. Wyłącz domyślne zachowanie „linku podobnego do skrótu”, wiążąc zdarzenie onclick z łączem i określając return false;jako wynik zdarzenia onclick.

Kiedy zaimplementowałem zakładki, napisałem coś takiego:

    <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>
Siergiej Smirnow
źródło
6

Żadna z odpowiedzi nie działa dla mnie wystarczająco dobrze, widzę, że strona przeskakuje do zakotwiczenia, a następnie do góry w przypadku niektórych rozwiązań, niektóre odpowiedzi w ogóle nie działają, może się to zmienić przez lata. Mam nadzieję, że moja funkcja komuś pomoże.

/**
 * 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
źródło
Najlepsze rozwiązanie, jakie do tej pory znalazłem. I faktycznie dużo widziałem.
MarkSkayff
Dziękuję Ci za to! Również jedyne rozwiązanie, które zadziałało u mnie po wielu godzinach prób, ale najlepsze dla mnie jest to, że nie muszę usuwać hasha.
Chris Vecchio
To nie działa, jeśli scroller jest już na górze strony po załadowaniu strony, przewinie się w dół jak zwykle, a po zakończeniu strony przeskakuje na górę. Czy ktoś wie, jak to się dzieje?
robgha01
@ robgha01, upewnij się, że funkcja jest wykonywana tak szybko, jak to możliwe i nie czeka na żadne zdarzenie, to jest złe: $(document).ready(function () { preventAnchorScroll(); });po prostu wywołaj funkcję na samym początku JavaScript. Przetestowałem to teraz, działa dla mnie w Chrome, Firefox i Opera.
kdmitry
4

brudny CSS naprawia tylko pozostanie przewijany za każdym razem, gdy kotwica jest używana (nie przydatne, jeśli nadal chcesz używać kotwic do skoków przewijania, bardzo przydatne do głębokiego linkowania):

.elementsWithAnchorIds::before {
   content: "";
   display: block;
   height: 9999px;
   margin-top: -9999px; //higher thin page height
}
inny ty
źródło
baaaaardzo sprytne!
duhaime,
3

Chociaż zaakceptowana odpowiedź działa dobrze, zauważyłem, że czasami, szczególnie na stronach zawierających duże obrazy, pasek przewijania będzie szaleńczo przeskakiwać, używając

 window.scrollTo(0, 0);

Co może być dość rozpraszające dla użytkownika.

Rozwiązanie, które ostatecznie wybrałem, jest w rzeczywistości dość proste i polega na użyciu innego identyfikatora celu niż ten używany w location.hash

Na przykład:

Oto link na innej stronie

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

Więc oczywiście, jeśli istnieje element o identyfikatorze tab2 na stronie z zakładkami okno przeskoczy do niego po załadowaniu.

Możesz więc dołączyć coś do identyfikatora, aby temu zapobiec:

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

Następnie możesz dołączyć "-noScroll"do location.hashw 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>
Andrzej
źródło
2

W moim przypadku ze względu na użycie linku kotwicy do wyskakującego okienka CSS użyłem tego sposobu:

Po prostu zapisz stronę YOffset po kliknięciu, a następnie ustaw ScrollTop na to:

$(document).on('click','yourtarget',function(){
        var st=window.pageYOffset;
        $('html, body').animate({
                'scrollTop' : st
            });
});
Metro Polisue
źródło
Po wypróbowaniu innych rozwiązań to było to, które zadziałało dla mnie, w moim przypadku strona, z którą pracowałem, użyła znaku # w adresie URL do załadowania określonej sekcji kasy. Zamiast modyfikować funkcjonalność kasy dla skrótu w adresie URL, właśnie tego użyłem. Dzięki
chris c
1

Myślę, że znalazłem sposób na karty interfejsu użytkownika bez ponownego modyfikowania funkcjonalności. Jak dotąd nie znalazłem żadnych problemów.

    $( ".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);

Wszystko w ramach gotowego wydarzenia. Dodaje „tab_” jako hash, ale działa zarówno po kliknięciu, jak i po załadowaniu strony z hashem.

JRomero
źródło
1

To zadziała z bootstrapem 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
źródło
0

Inne podejście

Spróbuj sprawdzić, czy strona została przewinięta i dopiero wtedy zresetuj pozycję:

var scrolled = false;

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

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

Heres a demo

hitautodestruct
źródło
nie działa stary ... początkowe ładowanie jest na dole ... po odświeżeniu pozostaje na górze
Martin Zvarík
0

Nie odniosłem dużego sukcesu z powyższymi setTimeoutmetodami w przeglądarce Firefox.

Zamiast setTimeout użyłem funkcji onload:

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

Niestety nadal jest to bardzo glitchy.

bozdoz
źródło
ponieważ onload uruchamia się, gdy wszystko jest w pełni załadowane, w tym zdjęcia ... więc oczywiście jest to
błędne
0

Nie odniosłem stałego sukcesu z tymi rozwiązaniami.

Najwyraźniej ze względu na fakt, że skok następuje przed Javascript => odniesieniem ( http://forum.jquery.com/topic/preventing-anchor-jump )

Moim rozwiązaniem jest domyślne umieszczenie wszystkich kotwic na górze za pomocą CSS.

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

Następnie użyj jquery, aby przewinąć do rodzica docelowej kotwicy lub rodzeństwa (może to być pusty znacznik em)

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

Przykład jquery do przewijania po wprowadzeniu adresu URL z hashem
(strona nie może być już załadowana w oknie / karcie, obejmuje to linki z innych / zewnętrznych źródeł)

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

Będziesz także chciał zapobiec domyślnym kliknięciom kotwicy na stronie, a następnie przewinąć do ich celów

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

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

Zaletą tej metody jest to, że docelowe znaczniki kotwicy pozostają strukturalnie obok odpowiedniej treści, chociaż ich pozycja CSS znajduje się na górze.

Powinno to oznaczać, że roboty wyszukiwarek nie będą miały problemu.

Na zdrowie, mam nadzieję, że to pomoże
Grayowi

grayskale.com
źródło
0

Spróbuj tego, aby zapobiec wszelkiego rodzaju przewijaniu w pionie po zmianie hash (wewnątrz twojego modułu obsługi zdarzeń):

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

(przerysowanie przeglądarki)

andy
źródło
0

Rozwiązałem mój problem w ten sposób:

// 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
źródło