Popstateイベントが発生したときにドキュメントをスクロールするデフォルトの動作を防ぐことは可能ですか?
私たちのサイトはjQueryアニメーションスクロールとHistory.jsを使用しており、状態を変更すると、pushstateまたはpopstateを使用して、ページのさまざまな領域にユーザーをスクロールさせる必要があります。問題は、popstateイベントが発生すると、ブラウザーが以前の状態のスクロール位置を自動的に復元することです。
ドキュメントの幅と高さを100%に設定したコンテナ要素を使用して、そのコンテナ内のコンテンツをスクロールしてみました。私が見つけた問題は、文書をスクロールするほど滑らかではないようだということです。特に、ボックスシャドウやグラデーションのような多くのcss3を使用する場合。
また、ユーザーがスクロールを開始したときにドキュメントのスクロール位置を保存し、ブラウザーが(popstateで)ページをスクロールした後にそれを復元しようとしました。これはFirefox 12では正常に機能しますが、Chrome 19では、ページがスクロールおよび復元されるためにちらつきが発生します。 (スクロール位置が復元される場所)。
Firefoxは、popstateが起動する前にページをスクロール(およびscrollイベントを起動)し、Chromeが最初にpopstateを起動してからドキュメントをスクロールします。
履歴APIを使用するすべてのサイトは、上記と同様のソリューションを使用するか、ユーザーが戻る/進むときにスクロール位置の変更を無視します(GitHubなど)。
Popstateイベントでドキュメントがまったくスクロールされないようにすることはできますか?
if ('scrollRestoration' in history) {
history.scrollRestoration = 'manual';
}
( Google発表 2015年9月2日)
ブラウザのサポート:
Chrome:サポート(46以降)
Firefox:サポート(46以降)
IE/Edge:サポートされていません、 サポートを求めてください (リンクはコメントのまま)
Opera:サポート(33以降)
Safari:サポート
詳細については、「 MDNでのブラウザーの互換性 」を参照してください。
これは mozilla開発者コアで1年以上報告された問題 です。残念ながら、チケットは実際には進歩しませんでした。 Chromeは同じです。jsを介してスクロール位置onpopstate
に対処する信頼性の高い方法はありません。これはネイティブブラウザの動作であるためです。
ただし、状態オブジェクトでスクロール位置が明示的に表示されることを明示的に望んでいる HTML5履歴仕様 を見ると、将来への希望があります。
履歴オブジェクトは、ブラウジングコンテキストのセッション履歴を、セッション履歴エントリのフラットリストとして表します。各セッション履歴エントリは、URLおよびオプションで状態オブジェクトで構成され、さらにタイトル、ドキュメントオブジェクト、フォームデータ、スクロール位置、およびそれに関連付けられたその他の情報を持つことができます。
これと、上記のmozillaチケットに関するコメントを読むと、少なくともonpopstate
を使用しているユーザーに対して、近い将来スクロール位置がpushState
に戻らない可能性があることを示す兆候が得られます。 。
残念ながら、それまでは、pushState
を使用するとスクロール位置が保存され、replaceState
はスクロール位置を置き換えません。それ以外の場合は、かなり簡単です。ユーザーがページをスクロールするたびに(いくつかの慎重なonscrollハンドラーを使用して)replaceState
を使用して現在のスクロール位置を設定できます。
また、残念ながら、HTML5仕様はpopstate
イベントを正確に起動する必要があるタイミングを指定せず、単に"セッション履歴エントリにナビゲートするときに特定のケースで起動する"を明示します。または後;常に以前であれば、popstateの後に発生するスクロールイベントを処理するソリューションが可能です。
スクロールイベントをキャンセルしますか?
さらに、scrollイベントがキャンセル可能であれば、それも簡単ではありません。そうであれば、シリーズの最初のスクロールイベントをキャンセルするだけで済みます(ユーザースクロールイベントはレミングのようなもので、数十種類ありますが、履歴の再配置によって発生するスクロールイベントは1つです)。
今のところ解決策はありません
私が見る限り、私が今お勧めする唯一のことは、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%確信しています。そしてあなたは質問で言及した妥協の1つを使わなければならないでしょう。両方のブラウザーは、JavaScriptがそれについて何かを知る前にスクロールするため、JavaScriptはイベントの後にのみ反応できます。唯一の違いは、JavaScriptが起動するまでFirefoxが画面をペイントしないことです。そのため、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をmanualに設定しても機能しません。これが私の解決策です。
window.addEventListener('popstate', function(e) {
var scrollTop = document.body.scrollTop;
window.addEventListener('scroll', function(e) {
document.body.scrollTop = scrollTop;
});
});
他の人が言ったように、それを行うための本当の方法はなく、,いハック方法しかありません。スクロールイベントリスナーを削除しても機能しなかったので、これを行うためのwayい方法を次に示します。
/// 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に最初に戻ったときに点滅します)。改善の提案は大歓迎です!これが役立つことを願っています。
ページ上部のどこかに「スパン」要素を作成し、ロード時にこれにフォーカスを設定します。ブラウザは、フォーカスされた要素までスクロールします。これは回避策であり、すべてのブラウザ(uhmmm .. Safari)で「スパン」に焦点を当てることができないことを理解しています。お役に立てれば。
以下は、ポストステートが発生したときにスクロール位置を特定の要素にフォーカスしたいサイトに実装したものです(戻るボタン)。
$(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は位置までスクロールしますが、元の場所に戻ります。