Можно ли предотвратить стандартное поведение прокрутки документа при возникновении события popstate?
Наш сайт использует анимированную прокрутку jQuery и History.js, а изменения состояния должны прокручивать пользователя в разные области страницы с помощью pushstate или popstate. Проблема в том, что браузер автоматически восстанавливает позицию прокрутки предыдущего состояния при возникновении события popstate.
Я пробовал использовать элемент контейнера со 100% шириной и высотой документа и прокручивать содержимое внутри этого контейнера. Я обнаружил, что проблема заключается в том, что это не так гладко, как прокрутка документа; особенно при использовании большого количества CSS3, таких как тени и градиенты.
Я также пробовал сохранять позицию прокрутки документа во время прокрутки, инициированной пользователем, и восстанавливать ее после того, как браузер прокручивает страницу (в popstate). Это отлично работает в Firefox 12, но в Chrome 19 наблюдается мерцание из-за прокрутки и восстановления страницы. Я предполагаю, что это связано с задержкой между прокруткой и запуском события прокрутки (когда положение прокрутки восстанавливается).
Firefox прокручивает страницу (и запускает событие scroll) перед запуском popstate, а Chrome сначала запускает popstate, а затем прокручивает документ.
Все сайты, которые я видел, которые используют API истории, либо используют решение, подобное приведенному выше, либо просто игнорируют изменение позиции прокрутки, когда пользователь возвращается назад / вперед (например, GitHub).
Можно ли вообще предотвратить прокрутку документа при всплывающих событиях?
источник
document.body.clientHeight
но я не видел, чтобы он работал во всех браузерах.Ответы:
if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; }
( Объявление Google 2 сентября 2015 г.)
Поддержка браузера:
Chrome: поддерживается (с 46)
Firefox: поддерживается (с 46)
IE: не поддерживается
Edge: поддерживается (с 79)
Opera: поддерживается (с 33)
Safari: поддерживается
Для получения дополнительной информации см. Совместимость браузеров на MDN .
источник
history.scrollRestoration && history.scrollRestoration = 'manual';
или дажеvar sr = 'scrollRestoration'; history[sr] && history[sr] = 'manual';
Об этой проблеме с ядром разработчика Mozilla сообщалось уже больше года . К сожалению, тикет не продвинулся. Я думаю, что Chrome такой же: нет надежного способа справиться с позицией прокрутки
onpopstate
через js, поскольку это собственное поведение браузера.Однако есть надежда на будущее, если вы посмотрите на спецификацию истории HTML5 , которая явно желает, чтобы позиция прокрутки была представлена в объекте состояния:
Это, и если вы прочитаете комментарии к заявке Mozilla, упомянутые выше, дает некоторое указание на то, что возможно в ближайшем будущем положение прокрутки больше не будет восстановлено
onpopstate
, по крайней мере, для людей, использующихpushState
.К сожалению, до тех пор положение прокрутки сохраняется при
pushState
использовании иreplaceState
не заменяет положение прокрутки. В противном случае это было бы довольно просто, и вы могли бы использоватьreplaceState
для установки текущей позиции прокрутки каждый раз, когда пользователь прокручивает страницу (с некоторым осторожным обработчиком прокрутки).Также, к сожалению, в спецификации HTML5 не указано, когда именно
popstate
событие должно быть запущено, а просто сказано: «запускается в определенных случаях при переходе к записи в истории сеанса», в которой четко не указано, до или после; если бы это было всегда раньше, было бы возможно решение с обработкой события прокрутки, происходящего после popstate.Отменить событие прокрутки?
Кроме того, было бы легко, если бы событие прокрутки было отменяемым, а это не так. Если бы это было так, вы могли бы просто отменить первое событие прокрутки в серии (события прокрутки пользователя похожи на леммингов, они бывают десятками, тогда как событие прокрутки, запускаемое репозиционированием истории, является единственным), и все будет в порядке.
На данный момент нет решения
Насколько я понимаю, единственное, что я рекомендую на данный момент, - это дождаться полной реализации спецификации 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
позиционированного элемента, также исключается скоростью перерисовки экрана.Так что я на 99% уверен, что ответ заключается в том, что вы не можете, и вам придется использовать один из компромиссов, которые вы упомянули в вопросе. Оба браузера прокручивают страницу до того, как JavaScript что-нибудь об этом узнает, поэтому JavaScript может реагировать только после события. Единственное отличие состоит в том, что Firefox не рисует экран до тех пор, пока не сработает Javascript, поэтому работоспособное решение есть в Firefox, но не в WebKit.
источник
Теперь ты можешь сделать
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" не работает во всех браузерах (ммммм .. Safari). Надеюсь это поможет.
источник
Вот что я реализовал на сайте, который хотел, чтобы позиция прокрутки фокусировалась на определенном элементе при срабатывании 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; };
источник