HTML5履歴ポップステートでのブラウザのスクロールを防止する


83

popstateイベントが発生したときにドキュメントをスクロールするデフォルトの動作を防ぐことは可能ですか?

私たちのサイトはjQueryアニメーションスクロールとHistory.jsを使用しており、状態の変更により、pushstateまたはpopstateを介して、ページのさまざまな領域にユーザーをスクロールする必要があります。問題は、popstateイベントが発生すると、ブラウザが前の状態のスクロール位置を自動的に復元することです。

ドキュメントの幅と高さを100%に設定したコンテナ要素を使用して、そのコンテナ内のコンテンツをスクロールしてみました。私が見つけた問題は、ドキュメントのスクロールほどスムーズではないように見えることです。特にボックスシャドウやグラデーションのようなcss3をたくさん使用する場合。

また、ユーザーがスクロールを開始したときにドキュメントのスクロール位置を保存し、ブラウザーがページをスクロールした後にドキュメントを復元しようとしました(popstateで)。これはFirefox12では正常に機能しますが、Chrome 19では、ページがスクロールされて復元されるためにちらつきがあります。これは、スクロールとスクロールイベントが発生するまでの遅延(スクロール位置が復元される場所)に関係していると思います。

Firefoxはpopstateが起動する前にページをスクロールし(そしてscrollイベントを起動し)、Chromeは最初にpopstateを起動してからドキュメントをスクロールします。

履歴APIを使用している私が見たすべてのサイトは、上記と同様のソリューションを使用するか、ユーザーが前後に移動したときのスクロール位置の変更を無視します(GitHubなど)。

popstateイベントでドキュメントがスクロールされないようにすることは可能ですか?


スクロール位置を設定した後、(ページのリフローを強制するために)要素の寸法を読み取ることに成功する場合があります。たとえばdocument.body.clientHeight、すべてのブラウザで機能することは確認していません。
ショーンホーガン2012年

ドキュメントのスクロール機能を操作した場合はどうなりますか?少なくともそこから始めます。ここに解決策があります:stackoverflow.com/questions/7035331/…–
ジェフウッデン

回答:


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

(2015年9月2日にGoogle発表

ブラウザのサポート:

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開発者コアで1年以上報告されている問題です。残念ながら、チケットは実際には進行しませんでした。Chromeも同じだと思いますonpopstate。ブラウザのネイティブな動作であるため、jsを介してスクロール位置に取り組む信頼できる方法はありません。

ただし、HTML5の履歴仕様を見ると、将来の希望があります。これは、スクロール位置が状態オブジェクトに表されることを明示的に望んでいます。

履歴オブジェクトは、ブラウジングコンテキストのセッション履歴をセッション履歴エントリのフラットリストとして表します。各セッション履歴エントリは、URLとオプションで状態オブジェクトで構成され、さらに、タイトル、ドキュメントオブジェクト、フォームデータ、スクロール位置、およびその他の情報が関連付けられている場合があります。

これと、上記のMozillaチケットに関するコメントを読むと、onpopstate少なくともを使用している人にとっては、近い将来、スクロール位置が復元されなくなる可能性があることがわかりますpushState

残念ながら、それまでは、pushState使用時にスクロール位置が保存され、スクロール位置がreplaceState置き換えられることはありません。それ以外の場合は、かなり簡単でreplaceState、ユーザーがページをスクロールするたびに現在のスクロール位置を設定するために使用できます(注意深いonscrollハンドラーを使用)。

また、残念ながら、HTML5仕様では、popstateイベントをいつ発生させる必要があるかを正確に指定していません。「セッション履歴エントリに移動するときに特定の場合に発生する」とだけ記載されています。これは、イベントが発生する前か後かを明確に示していません。それが常に前であった場合、ポップステートの後に発生するスクロールイベントを処理するソリューションが可能です。

スクロールイベントをキャンセルしますか?

さらに、スクロールイベントがキャンセル可能であれば、それは簡単ではありません。もしそうなら、シリーズの最初のスクロールイベントをキャンセルするだけでよく(ユーザーのスクロールイベントはレミングスのようなもので、数十個ありますが、履歴の再配置によって発生するスクロールイベントは1つです)、問題ありません。

今のところ解決策はありません

私が見る限り、今のところ私がお勧めするのは、HTML5仕様が完全に実装されるのを待って、この場合はブラウザーの動作に合わせてロールすることだけです。つまり、ブラウザーでスクロールが許可されたときにスクロールをアニメーション化することです。 、履歴イベントが発生したときにブラウザにページの位置を変更させます。位置に関して影響を与えることができる唯一のことはpushState、ページが戻るのに良い方法で配置されているときに使用することです。他の解決策は、バグがあるか、ブラウザ固有であるか、またはその両方である必要があります。


履歴仕様の文言が少し異なります。さらに、下にスクロールしてpushStateのドキュメントを見ると、スクロール位置を追加することについては言及されていません。仕様では、セッション履歴エントリに保存されたスクロール位置が含まれている可能性があることを示唆していますが、それがWeb開発者がそれを設定できることを意味するかどうかはわかりません。
Walex 2014年

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%確信しています、そしてあなたはあなたが質問で言及した妥協の1つを使わなければならないでしょう。どちらのブラウザも、JavaScriptがそれについて何も知る前にスクロールするため、JavaScriptはイベント後にのみ反応できます。唯一の違いは、FirefoxはJavascriptが起動するまで画面をペイントしないことです。そのため、Firefoxには実行可能なソリューションがありますが、WebKitにはありません。


3

今、あなたはすることができます

history.scrollRestoration = 'manual';

これにより、ブラウザのスクロールが防止されます。これは現在Chrome46以降でのみ機能しますが、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をmanualに設定しても機能しません。これが私の解決策です。

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

0

ページ上部のどこかに「span」要素を作成し、ロード時にこれにフォーカスを設定します。ブラウザは、フォーカスされた要素までスクロールします。これは回避策であり、「スパン」に焦点を当てることがすべてのブラウザで機能するとは限らないことを理解しています(うーん..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に初めて戻ったときに点滅します)。改善の提案は大歓迎です!お役に立てれば。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.