Запобігання прокрутці браузера у спливаючому списку історії HTML5


83

Чи можна запобігти поведінці за замовчуванням при прокрутці документа, коли відбувається подія popstate?

Наш сайт використовує анімоване прокручування jQuery та History.js, а зміни стану повинні прокручувати користувача по різних областях сторінки, за допомогою pushstate або popstate. Проблема полягає в тому, що браузер автоматично відновлює позицію прокрутки попереднього стану, коли відбувається поп-стан.

Я спробував використати елемент контейнера, встановлений на 100% ширини та висоти документа, та прокрутити вміст всередині цього контейнера. Проблема, яку я виявив, полягає в тому, що вона, здається, не настільки гладка, як прокрутка документа; особливо якщо використовується велика кількість css3, наприклад, тіні та градієнти.

Я також спробував зберегти позицію прокрутки документа під час прокрутки, ініційованої користувачем, і відновити її після того, як браузер прокручує сторінку (у popstate). Це добре працює у Firefox 12, але в Chrome 19 з’являється мерехтіння через прокручування та відновлення сторінки. Я припускаю, що це пов’язано із затримкою між прокруткою та спрацьованою подією прокрутки (де позиція прокрутки відновлюється).

Firefox прокручує сторінку (і запускає подію прокрутки) перед запуском popstate, а Chrome запускає popstate, а потім прокручує документ.

Усі сайти, які я бачив, що використовують API історії, використовують або рішення, подібне до наведених вище, або просто ігнорують зміну позиції прокрутки, коли користувач повертається вперед / назад (наприклад, GitHub).

Чи можна запобігти прокручуванню документа взагалі на виставках, що проводяться у державній залі?


Ви можете досягти певного успіху з читанням розміру елемента (щоб змусити переформатувати сторінку) після встановлення положення прокрутки. наприклад, document.body.clientHeightале я не бачив, як це працює у всіх браузерах.
Шон Хоган

Що, якби ви маніпулювали функцією прокрутки документа, саме з цього я б почав щонайменше. Тут є рішення для нього: stackoverflow.com/questions/7035331/…
Jeff Wooden

Відповіді:


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

( Оголошено Google 2 вересня 2015 р.)

Підтримка браузера:

Chrome: підтримується (з 46)

Firefox: підтримується (з 46)

IE / Edge: не підтримується, попросіть підтримки (потім залиште посилання в коментарі)

Opera: підтримується (з 33)

Safari: підтримується

Для отримання додаткової інформації див. Сумісність браузера з MDN .


1
Коротше: history.scrollRestoration && history.scrollRestoration = 'manual';чи навітьvar sr = 'scrollRestoration'; history[sr] && history[sr] = 'manual';
Бхарата

31

Це було тепер повідомляється проблема з ядром розробників Mozilla для більш року . На жаль, квиток насправді не просунувся. Я думаю, що Chrome однаковий: немає надійного способу вирішити позицію прокрутки onpopstateчерез js, оскільки це поведінка власного браузера.

Однак є надія на майбутнє, якщо ви подивитесь на специфікацію історії HTML5 , яка явно бажає, щоб позиція прокрутки була представлена ​​в об’єкті стану:

Об'єкти історії представляють історію сеансів контексту перегляду у вигляді плоского списку записів історії сесій. Кожен запис історії сеансу складається з URL-адреси та, за бажанням, об’єкта стану, і, крім того, може мати заголовок, об’єкт Документа, дані форми, позицію прокрутки та іншу інформацію, пов’язану з нею.

Це, і якщо ви прочитаєте коментарі до згаданого вище квитка на mozilla, це вказує на те, що найближчим часом позиція прокрутки більше не буде відновлена onpopstate, принаймні для людей, які використовують pushState.

На жаль, до тих пір позиція прокрутки зберігається, коли pushStateвикористовується, і replaceStateне замінює позицію прокрутки. В іншому випадку це було б досить просто, і ви могли б використовувати replaceStateдля встановлення поточної позиції прокрутки кожного разу, коли користувач прокручував сторінку (з деяким обережним обробником прокрутки).

Також, на жаль, специфікація HTML5 не вказує, коли саме popstateпотрібно запускати подію, вона просто говорить: «спрацьовує в певних випадках при переході до запису історії сеансу», де чітко не сказано, до або після; якби це було раніше, було б можливим рішення з обробкою події прокрутки, що відбулася після спливаючого стану.

Скасувати подію прокрутки?

Крім того, було б також легко, якби подія прокрутки була скасована, а це ні. Якби це було, ви могли б просто скасувати першу подію прокрутки серії (події прокрутки користувача схожі на лемінги, їх буває десятки, тоді як подія прокрутки, яку запускає репозиціонування історії, є однією), і у вас все буде добре.

Наразі рішення немає

Наскільки я бачу, єдине, що я б порекомендував наразі, це зачекати, поки HTML5 Spec буде повністю реалізовано, і скотитися з поведінкою браузера в цьому випадку, це означає: анімувати прокрутку, коли браузер дозволяє вам це робити , і дозвольте браузеру змінити положення сторінки, коли відбулася подія історії. Єдине, на що ти можеш впливати позиційно, це те, що ти використовуєш, pushStateколи сторінка позиціонується належним чином для повернення. Будь-яке інше рішення або повинно мати помилки, або бути занадто специфічним для браузера, або і те, і інше.


Формулювання специфікації історії зараз дещо інше. Крім того, якщо ви прокручуєте вниз і переглядаєте документацію pushState, немає жодної згадки про додавання позиції прокрутки. Специфікація передбачає, що запис історії сеансів може містити збережену позицію прокрутки, але я не впевнений, що це означає, що веб-розробник зможе її встановити.
Walex

4

Вам доведеться використовувати тут якийсь жахливий нюхач браузера. Що стосується Firefox, я б запропонував ваше рішення щодо збереження положення прокрутки та відновлення його.

Я думав, що маю хороше рішення Webkit, засноване на вашому описі, але я щойно спробував у Chrome 21, і, схоже, Chrome спочатку прокручується, потім запускає подію popstate, потім запускає подію прокрутки. Але для довідки, ось що я придумав:

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

Чорна магія, така як прикидання сторінки прокручується за допомогою переміщення absoluteпозиціонованого елемента, також виключається швидкістю перефарбовування екрану.

Тож я на 99% впевнений, що відповідь полягає в тому, що ви не можете, і вам доведеться скористатися одним із компромісів, про які ви згадали у питанні. Обидва браузери прокручують, перш ніж JavaScript про це щось знає, тому JavaScript може реагувати лише після події. Єдина відмінність полягає в тому, що Firefox не фарбує екран до моменту запуску Javascript, саме тому у Firefox є дієве рішення, але не в WebKit.


3

Тепер ви можете це зробити

history.scrollRestoration = 'manual';

і це має запобігти прокрутці браузера. Зараз це працює лише в Chrome 46 і новіших версіях, але, схоже, Firefox планує його також підтримати


2

Рішення полягає у використанні position: fixedта вказівці topрівної позиції прокрутки сторінки.

Ось приклад:

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

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

Так, замість цього ви отримуєте мерехтливу смугу прокрутки, але це менше зла.


1

Наступне виправлення має працювати у всіх браузерах.

Ви можете встановити положення прокрутки на 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);
});

1

Встановлення scrollRestoration у ручному режимі для мене не спрацювало, ось моє рішення.

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

0

Створіть елемент 'span' десь у верхній частині сторінки та встановіть фокус на це при завантаженні. Браузер перейде до елемента, що фокусується. Я розумію, що це обхідне рішення, і зосередження уваги на “span” працює не у всіх браузерах (uhmmm .. Safari). Сподіваюся, це допомагає.


0

Ось те, що я реалізував на сайті, який хотів, щоб позиція прокрутки фокусувалася на певному елементі під час запуску постстату (кнопка "Назад"):

$(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 прокрутить до позиції, але потім повернеться до початкового місця.


0

Як казали інші, немає реального способу зробити це, є лише потворний хакерський спосіб. Видалення прослуховувача події прокрутки у мене не працювало, тому ось мій потворний спосіб це зробити:

/// 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). Будь-які пропозиції щодо вдосконалення вітаються! Сподіваюся, це допомагає.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.