Запретить прокрутку браузера на HTML5 история popstate


можно ли предотвратить поведение по умолчанию для прокрутки документа при возникновении события popstate?

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

Я пробовал использовать элемент контейнера, установленный на 100% ширину и высота документа и прокрутка содержимого внутри этого контейнера. Проблема с тем, что я нашел, это не кажется почти таким же гладким, как прокрутка документа; особенно если используется много css3, таких как box-shadows и градиенты.

Я также попытался сохранить положение прокрутки документа во время прокрутки, инициированной пользователем, и восстановить его после прокрутки страницы браузером (на popstate). Это прекрасно работает в Firefox 12, но в Chrome 19 есть мерцание из-за страницы прокручивается и восстанавливается. Я предполагаю, что это связано с задержкой между прокруткой и запускаемым событием прокрутки (где положение прокрутки восстанавливается).

Firefox прокручивает страницу (и запускает событие прокрутки) перед запуском popstate, а Chrome сначала запускает popstate, а затем прокручивает документ.

все сайты, которые я видел, которые используют API истории, либо используют решение, подобное приведенным выше, либо просто игнорируют изменение положения прокрутки, когда пользователь возвращается назад / вперед (например GitHub).

можно ли вообще запретить прокрутку документа в событиях popstate?

11 71

11 ответов:

if ('scrollRestoration' in history) {
  history.scrollRestoration = 'manual';
}

(объявлено Google 2 сентября 2015 года)

поддержка браузера:

Chrome: поддерживается (начиная с 46)

Firefox: будет поддерживаться с 19 апреля 2016 года (версия 46.0)

IE / Edge:попросите поддержки (тогда оставьте ссылку в комментарии)

Opera: поддерживается (начиная с 33)

дополнительная информация о MDN.

это сообщается о проблеме с ядром разработчика mozilla уже более года. К сожалению, билет не очень продвинулся. Я думаю, что Chrome-это то же самое: нет надежного способа решить положение прокрутки onpopstate через js, так как это собственное поведение браузера.

есть надежда на будущее, хотя, если посмотреть на HTML5 history spec, который явно желает, чтобы положение прокрутки было представлено в состоянии объект:

объекты истории представляют историю сеансов своего контекста просмотра в виде плоского списка записей истории сеансов. Каждая запись истории сеансов состоит из URL-адреса и, возможно, объекта состояния, а также может иметь заголовок, объект документа, данные формы, положение прокрутки и другую информацию, связанную с ним.

это, и если Вы читаете комментарии к билету mozilla, упомянутому выше, дает некоторые указания на то, что возможно, что в ближайшее время положение прокрутки больше не будет восстановлено onpopstate, по крайней мере для людей, используя pushState.

к сожалению, до тех пор, положение прокрутки сохраняется, когда pushState и replaceState не заменяет положение прокрутки. В противном случае, это было бы довольно легко, и вы могли бы использовать replaceState чтобы установить текущую позицию прокрутки каждый раз, когда пользователь прокручивает страницу (с некоторым осторожным обработчиком onscroll).

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

отменить событие Scroll?

кроме того, он также будет легко, если событие Scroll когда сократима, что это не так. Если он был, вы можно просто отменить первое событие прокрутки серии (события прокрутки пользователя похожи на лемминги, они приходят десятками, тогда как событие прокрутки, вызванное перемещением истории, является одним), и вы будете в порядке.

пока нет решения

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

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

Я думал, что у меня есть хорошее решение Webkit, основанное на вашем описании, но я просто попробовал в Chrome 21, и кажется, что Chrome сначала прокручивает, а затем запускает событие popstate, а затем запускает событие прокрутки. Но для справки, вот что я придумал:

function noScrollOnce(event) {
    event.preventDefault();
    document.removeEventListener('scroll', noScrollOnce);
}
window.onpopstate = function () {
    document.addEventListener('scroll', noScrollOnce);
};​

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

теперь вы можете сделать

history.scrollRestoration = 'manual';

и это должно предотвратить прокрутки браузера. Это работает только сейчас в Chrome 46 и выше, но кажется, что Firefox поддерживает его слишком

решение заключается в использовании position: fixed и указать top равно положению прокрутки страницы.

вот пример:

$(window).on('popstate', function()
{
   $('.yourWrapAroundAllContent').css({
      position: 'fixed',
      top: -window.scrollY
   });

   requestAnimationFrame(function()
   {
      $('.yourWrapAroundAllContent').css({
         position: 'static',
         top: 0
      });          
   });
});

Да, вы вместо этого получаете мерцающую полосу прокрутки, но это меньше зла.

исправление должно работать во всех браузерах.

вы можете установить положение прокрутки в 0 на событие выгрузки. Вы можете прочитать об этом событии здесь:https://developer.mozilla.org/en-US/docs/Web/Events/unload. по сути, событие выгрузки срабатывает прямо перед тем, как вы покинете страницу.

установив scrollPosition в 0 при выгрузке означает, что когда вы покидаете страницу с установленным pushState он устанавливает scrollPosition в 0. Когда вы вернетесь на эту страницу, обновив или нажатие назад не будет автоматически прокручиваться.

//Listen for unload event. This is triggered when leaving the page.
//Reference: https://developer.mozilla.org/en-US/docs/Web/Events/unload
window.addEventListener('unload', function(e) {
    //set scroll position to the top of the page.
    window.scrollTo(0, 0);
});

установка scrollRestoration в ручную не работала для меня, вот мое решение.

window.addEventListener('popstate', function(e) {
    var scrollTop = document.body.scrollTop;
    window.addEventListener('scroll', function(e) {
        document.body.scrollTop = scrollTop;
    });
});

создайте элемент "span" где-то в верхней части страницы и установите фокус на это при загрузке. Браузер будет прокручивать к сфокусированному элементу. Я понимаю, что это обходной путь, и фокус на "span" не работает во всех браузерах ( uhmmm.. Сафари.) Надеюсь, это поможет.

вот что я реализовал на сайте, который хотел, чтобы положение прокрутки фокусировалось на определенном элементе при запуске poststate (кнопка Назад):

$(document).ready(function () {

     if (window.history.pushState) {
        //if push supported - push current page onto stack:
        window.history.pushState(null, document.title, document.location.href);
    }

    //add handler:
    $(window).on('popstate', PopStateHandler);
}

//fires when the back button is pressed:
function PopStateHandler(e) {
    emnt= $('#elementID');
    window.scrollTo(0, emnt.position().top);
    alert('scrolling to position');
}

протестировано и работает в firefox.

Chrome будет прокручиваться до позиции, но затем перемещается обратно в исходное место.

Как говорили другие, нет никакого реального способа сделать это, только уродливый хакерский способ. Удаление прослушивателя событий прокрутки не сработало для меня, поэтому вот мой уродливый способ сделать это:

/// GLOBAL VARS ////////////////////////
var saveScrollPos = false;
var scrollPosSaved = window.pageYOffset;
////////////////////////////////////////

jQuery(document).ready(function($){

    //// Go back with keyboard shortcuts ////
    $(window).keydown(function(e){
        var key = e.keyCode || e.charCode;

        if((e.altKey && key == 37) || (e.altKey && key == 39) || key == 8)
        saveScrollPos = false;
    });
    /////////////////////////////////////////

    //// Go back with back button ////
    $("html").bind("mouseout",  function(){ saveScrollPos = false; });
    //////////////////////////////////

    $("html").bind("mousemove", function(){ saveScrollPos = true; });

    $(window).scroll(function(){
        if(saveScrollPos)
        scrollPosSaved = window.pageYOffset;
        else
        window.scrollTo(0, scrollPosSaved);
    });

});

он работает в Chrome, FF и IE (он мигает в первый раз, когда вы возвращаетесь в IE). Любые предложения по улучшению приветствуются! Надеюсь, это поможет.

может хотите попробовать это?

window.onpopstate = function(event) {
  return false;
};