Как отключить «прыжок» якоря при загрузке страницы?

111

Я думаю, что это может быть невозможно, постараюсь объяснить как можно лучше. У меня есть страница, содержащая вкладки (с поддержкой jquery), которые контролируются следующим:

Я использую этот код, предоставленный другим пользователем из предыдущего вопроса.

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

этот код отлично работает, когда я напрямую перехожу на страницу "вкладок".

однако мне нужно связать отдельные вкладки с других страниц - для этого в коде window.location.hashотображается соответствующая вкладка.

страница не "перескакивает" на якорь из-за "return false".

Однако это событие запускается только при событии щелчка. следовательно, если я захожу на свои «вкладки» с любой другой страницы, срабатывает эффект «прыжка». Чтобы бороться с этим, я автоматически прокручиваю страницу до начала, но я бы предпочел, чтобы этого не происходило.

есть ли способ имитировать "return false" при загрузке страницы, предотвращая "прыжок" привязки.

надеюсь, это достаточно ясно.

Спасибо

Росс
источник

Ответы:

144

Ваше исправление не работает? Не уверен, правильно ли я понял вопрос - у вас есть демонстрационная страница? Вы можете попробовать:

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

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

Изменить: протестировано и работает в Firefox, IE и Chrome в Windows.
Изменить 2: переместить setTimeout()внутрь ifблока, реквизиты @vsync.

dave1010
источник
2
герой! Да, мое «исправление» сработало, но страница будет «прокручиваться» (как сказал мой код), и я бы предпочел, чтобы она не прокручивалась. ваш код работает отлично. Я не смотрел ScrollTo - функция вообще нужна? похоже, он отлично работает только с window.scrollTo (0, 0);
Росс
3
Я пробовал это в Firefox на пустой странице без символа, setTimeoutи это не всегда работало. Возможно, он вам и не понадобится, если он есть в jQuery $(function() {. На самом деле вам не нужен location.hash, но приятно оставить его, чтобы браузерам не пришлось ничего делать. Также напоминает вам, для чего предназначен scrollTo!
dave1010 07
2
Сегодня у меня была такая же проблема. Мне действительно пришлось добавить хеш (идентичный именам вкладок / значениям href) к моим навигационным ссылкам. В итоге я добавил суффикс из 1 символа к своим вкладкам, а затем подстроку значений на 1 символ (str.slice (0, -1) при сравнении их с window.location.hash Таким образом, хеши разные, и прыжков не происходит .
developer10
1
Он ifдолжен быть снаружи, а не внутри. Кроме того, минимум для интервала составляет около 10, поэтому 0 или 1 то же самое, что и 10., в зависимости от браузера.
vsync
2
Это НЕ мешает браузеру перейти к хэштегу, вы должны поставить пустой хэштег, чтобы предотвратить «скачкообразие», см. Мой ответ здесь stackoverflow.com/a/29823913/826194
Ларзан
43

См. Мой ответ здесь: Ответ на Stackoverflow

Хитрость заключается в том, чтобы как можно скорее удалить хэштег и сохранить его значение для собственного использования:

Важно, чтобы вы не помещали эту часть кода в функции $ () или $ (window) .load (), так как было бы слишком поздно и браузер уже перешел к тегу.

// 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
Я столкнулся с ошибкой с FF, когда я использовал переполнение max-height-y в div с очень длинным списком, FF прокручивал вниз, где скрытый узел будет в dom. При этом использовались только три верхние строки фактического кода (а не комментарии), и это исправило мою проблему с FireFox.
JQII
12

Есть и другие способы отслеживать, на какой вкладке вы находитесь; возможно установка файла cookie или значения в скрытом поле и т. д. и т. д.

Я бы сказал, что если вы не хотите, чтобы страница прыгала при загрузке, вам лучше использовать один из этих других вариантов, а не хеш, потому что основная причина использования хеша вместо них - разрешить именно то, что вы Заблокировать.

Еще один момент - страница не будет перескакивать, если ваши хеш-ссылки не совпадают с именами тегов в документе, поэтому, возможно, если вы хотите продолжать использовать хеш-код, вы можете манипулировать содержимым, чтобы теги назывались по-другому. Если вы используете согласованный префикс, вы все равно сможете использовать Javascript для перехода между ними.

Надеюсь, это поможет.

Spudley
источник
2
Хорошие моменты. Не забывайте стараться, чтобы страница оставалась доступной / используемой с выключенными JS / CSS.
dave1010 07
12

Я использовал решение dave1010, но оно было немного нервным, когда я поместил его в функцию $ (). 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. Браузер переходит на, <element id="abc" />если в адресе есть хэш http://site.com/#abc и отображается элемент с id = "abc". Таким образом, вы должны просто скрыть элемент по умолчанию: <element id="anchor" style="display: none" />. Если на странице нет видимого элемента с id = hash, браузер не будет прыгать.

  2. Создайте сценарий, который проверяет хэш в URL-адресе, а затем находит и отображает элемент prrpopriate (который по умолчанию скрыт).

  3. Отключите поведение «хеш-подобной ссылки» по умолчанию, привязав событие onclick к ссылке и указав return false;как результат события onclick.

Когда я реализовал вкладки, я написал что-то вроде этого:

    <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

Ни один из ответов не работает для меня достаточно хорошо, я вижу, как страница перескакивает на якорь, а затем вверх для некоторых решений, некоторые ответы вообще не работают, возможно, что-то изменилось за годы. Надеюсь, моя функция кому-то поможет.

/**
 * 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
        );
    });
}
кдмитрий
источник
Лучшее решение, которое я нашел до сих пор. И действительно видел многое.
MarkSkayff
Спасибо тебе за это! Также единственное решение, которое сработало для меня после нескольких часов попыток, но лучшая часть этого решения для меня - это то, что мне не нужно удалять хеш.
Крис Веккио,
Это не работает, если ползунок уже находится в верхней части страницы при загрузке страницы, тогда он будет прокручиваться вниз, как обычно, и после того, как страница будет завершена, он перейдет наверх. Кто-нибудь знает, как это обстоит дело?
robgha01,
@ robgha01, пожалуйста, убедитесь, что функция выполняется как можно скорее и не ожидает каких-либо событий, это неправильно: $(document).ready(function () { preventAnchorScroll(); });просто вызовите функцию в самом начале вашего JavaScript. Протестировал сейчас, у меня работает в Chrome, Firefox и Opera.
kdmitry
4

Исправлен только грязный CSS, чтобы прокрутка оставалась прокручиваемой каждый раз при использовании якоря (бесполезно, если вы все еще хотите использовать якоря для прокрутки-переходов, очень полезно для диплинков):

.elementsWithAnchorIds::before {
   content: "";
   display: block;
   height: 9999px;
   margin-top: -9999px; //higher thin page height
}
anotheryou
источник
оооочень умно!
Duhaime
3

Хотя принятый ответ работает хорошо, я обнаружил, что иногда, особенно на страницах, содержащих большие изображения, полоса прокрутки будет дико прыгать, используя

 window.scrollTo(0, 0);

Что может сильно отвлекать пользователя.

Решение, на котором я остановился в конце, на самом деле довольно простое, и заключается в использовании другого идентификатора цели, отличного от того, который используется в location.hash

Например:

Вот ссылка на другой странице

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

Так что, конечно, если tab2на странице вкладок есть элемент с идентификатором, окно перейдет к нему при загрузке.

Таким образом, вы можете добавить что-нибудь к идентификатору, чтобы предотвратить это:

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

И тогда вы можете добавить "-noScroll"к location.hashв 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

В моем случае из-за использования ссылки привязки для всплывающего окна CSS я использовал такой способ:

Просто сохраните pageYOffset первым делом при нажатии, а затем установите для этого ScrollTop:

$(document).on('click','yourtarget',function(){
        var st=window.pageYOffset;
        $('html, body').animate({
                'scrollTop' : st
            });
});
Метро Полисуэ
источник
После того, как я попробовал другие решения, это было то, что сработало для меня, в моем случае веб-сайт, с которым я работал, использовал # в URL-адресе для загрузки определенного раздела оформления заказа. Вместо того, чтобы изменять функцию проверки для хэша в URL-адресе, я только что использовал это. Спасибо
chris c
1

Я думаю, что нашел способ использовать вкладки пользовательского интерфейса, не переделывая функциональность. Пока проблем не обнаружил.

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

Все в рамках готового мероприятия. Он добавляет «tab_» к хешу, но работает как при нажатии, так и при загрузке страницы с хешем.

JRomero
источник
1

Это будет работать с 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
источник
0

Другой подход

Попробуйте проверить, прокручена ли страница, и только потом сбросьте позицию:

var scrolled = false;

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

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

Heres a demo

hitautodestruct
источник
не работает, братан ... начальная загрузка внизу ... после обновления она остается наверху
Мартин Зварик
0

Я не добился большого успеха в использовании вышеуказанных setTimeoutметодов в Firefox.

Вместо setTimeout я использовал функцию onload:

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

К сожалению, это все еще очень глючно.

боздоз
источник
потому что onload запускается после того, как все полностью загружено, включая фотографии ... так что, конечно, это
глючно
0

У меня не было стабильного успеха с этими решениями.

Видимо из-за того, что переход происходит до ссылки Javascript => ( http://forum.jquery.com/topic/preventing-anchor-jump )

Мое решение - расположить все якоря вверху по умолчанию с помощью CSS.

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

Затем используйте jquery для прокрутки до родителя целевого якоря или брата (может быть, пустой тег em)

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

Пример jquery для прокрутки, когда URL-адрес вводится с хешем
(страница не должна быть уже загружена в окне / вкладке, это охватывает ссылки из других / внешних источников)

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

Также вы захотите запретить по умолчанию щелчки привязки на странице, а затем прокрутить до их целей.

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

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

Преимущество этого метода в том, что целевые теги привязки остаются структурно рядом с релевантным контентом, хотя их положение CSS находится наверху.

Это должно означать, что у поисковых роботов не будет проблем.

Ура, надеюсь, это поможет
Грею

grayskale.com
источник
0

Попробуйте это, чтобы предотвратить вертикальную прокрутку клиента при изменении хэша (внутри вашего обработчика событий):

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

(перерисовка браузера)

Энди
источник
0

Решил мой промлем, сделав это:

// 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
источник