スクロールイベントがユーザーによって作成されたかどうかを検出します


82

スクロールイベントがブラウザによって行われたのか、ユーザーによって行われたのかを判断することはできますか?具体的には、戻るボタンを使用すると、ブラウザは最後の既知のスクロール位置にジャンプする場合があります。スクロールイベントにバインドした場合、これがユーザーによるものかブラウザによるものかをどのように判断できますか?

$(document).scroll( function(){ 
    //who did this?!
});

ブラウザでスクロールを引き起こす3つのタイプの状況があります。

  1. ユーザーが何らかのアクションを実行します。たとえば、マウスホイール、矢印キー、Page Up / Downキー、Home / Endキーを使用します。
  2. ブラウザは自動的にスクロールします。たとえば、ブラウザで戻るボタンを使用すると、最後の既知のスクロール位置に自動的にジャンプします。
  3. Javascriptがスクロールします。たとえば、element.scrollTo(x,y)

1
あなたの質問からはわかりませんが、ブラウザまたはユーザーのスクロールイベントに戻るボタンを使用してジャンプすることを検討してください。一般的に:「ブラウザによるスクロール」とは何だと思いますか?スクリプトによって開始されるスクロールを意味する場合は、スクリプトがスクロールするときに、イベントハンドラーを非アクティブ化するか、イベントハンドラーがそれを無視するようにフラグを設定するだけです。
roToRa 2011

戻るボタンでスクロールするのは「ブラウザスクロール」だと思いました。それ以外のもの-マウスホイール、上/下矢印、中央ボタンのクリックなどは、ユーザーのスクロールになります。私の本当の質問は、イベントがどこから来たのかを区別する方法はありますか?イベントオブジェクトのプロパティを確認しましたが、何も見つかりませんでした。私が想像できる3つのシナリオは、ブラウザーが開始するスクロール、JavaScriptが開始するスクロール、およびユーザーが開始するスクロールです。それが物事をより明確にすることを願っています。
mrtsherman 2011

同じ出力を達成しながら@mrtsherman私はこれらのいくつかを見つけた: stackoverflow.com/questions/2834667/...
AlphaMale

回答:


30

残念ながら、それを直接伝える方法はありません。

このタイプのフローに依存しないようにアプリを再設計できる場合は、それを選択してください。

そうでない場合、私が考えることができる回避策は、ユーザーが開始したスクロールを追跡し、スクロールがブラウザーまたはユーザーによってトリガーされたかどうかを確認することです。

これは私がまとめた例で、これはかなりうまくいきます(jQueryの履歴に問題があるブラウザーを除く)。

完全にテストできるようにするには、これをローカルで実行する必要があります(jsFiddle / jsbinはコンテンツをiFrameするため、適切ではありません)。

検証したテストケースは次のとおりです。

  • ページの読み込み-userScrollですfalse
  • マウス/キーボードを使用してスクロール-にuserScrollなりますtrue
  • リンクをクリックしてページ下部にジャンプします-にuserScrollなりますfalse
  • [戻る/進む]をクリックします-にuserScrollなりfalseます;

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="utf-8" /> 
    <script src="http://code.jquery.com/jquery-1.6.1.min.js"></script> 
    <script type="text/javascript" src="https://raw.github.com/tkyk/jquery-history-plugin/master/jquery.history.js"></script> 
</head> 
<body> 
    <span> hello there </span><br/> 
    <a href="#bottom"> click here to go down </a> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/><br/> 
    <a name="bottom"> just sitting </a> 
</body> 
<script type="text/javascript"> 

var userScroll = false;     

function mouseEvent(e) { 
    userScroll = true; 
} 


$(function() { 

    // reset flag on back/forward 
    $.history.init(function(hash){ 
        userScroll = false; 
    }); 

    $(document).keydown(function(e) { 
        if(e.which == 33        // page up 
           || e.which == 34     // page dn 
           || e.which == 32     // spacebar
           || e.which == 38     // up 
           || e.which == 40     // down 
           || (e.ctrlKey && e.which == 36)     // ctrl + home 
           || (e.ctrlKey && e.which == 35)     // ctrl + end 
          ) { 
            userScroll = true; 
        } 
    }); 

    // detect user scroll through mouse
    // Mozilla/Webkit 
    if(window.addEventListener) {
        document.addEventListener('DOMMouseScroll', mouseEvent, false); 
    }

    //for IE/OPERA etc 
    document.onmousewheel = mouseEvent; 


    // to reset flag when named anchors are clicked
    $('a[href*=#]').click(function() { 
        userScroll = false;
    }); 

      // detect browser/user scroll
    $(document).scroll( function(){  
        console.log('Scroll initiated by ' + (userScroll == true ? "user" : "browser"));
    });
}); 
</script> 
</html>

ノート:

  • これは、ユーザーがスクロールバーをマウスでドラッグしたときのスクロールを追跡しません。これは、私があなたのための演習として残したいくつかのコードで追加することができます。
  • event.keyCodes OSによって異なる場合があるため、適切に変更する必要がある場合があります。

お役に立てれば!


Mrchiefありがとうございます。私が望んでいたものではありませんが、これが最良の答えだと思います(何ですか?event.throwerプロパティがありませんか?!)。
mrtsherman 2011

ホームキーと終了キーを使用してスクロールするときに、コントロールを押す必要はありません。
カーティス

DOMMouseScrollは、Mac上の最新のChromeでは機能しません。onmousewheelを設定する代わりにdocument.addEventListener( 'mousewheel'を使用するのが最適
Curtis

8

私の知る限り、スクロールイベントが「ユーザー」または他の手段によって発行されたときはいつでも(何の作業もなしに)知ることは不可能です。

(他の人が述べたように)マウスホイールイベントをキャッチしてから、スクロールをトリガーできるキー(矢印、スペースなど)でキーダウンイベントをキャッチして、現在フォーカスされている要素を確認することができます。たとえば、を使用してスクロールすることはできません。入力フィールドに入力する際の矢印キー。一般に、これは複雑で面倒なスクリプトになります。

対処している状況に応じて、「ロジックを元に戻す」と推測できます。ユーザーが発行したスクロールイベントを検出する代わりに、プログラムで作成されたスクロールにフックして、コードで作成されていないスクロールイベントをユーザーが作成したものとして扱います。私が言ったように、それは状況とあなたが達成しようとしていることによって異なります。


WTKに感謝します。これはしばらくの間最良の答えでしたが、残念ながら、Mrchiefはいくつかのコード例であなたの答えを説明したので、私は彼に恩恵を与えました。私がそれを分割することができたなら、私は持っているでしょう!
mrtsherman 2011

4
それで大丈夫です。それは報奨金ではなく、知識を広め、獲得することです:)
WTK 2011

8

すべてのユーザーイベントをキャッチしようとするよりも、反対のことを実行してプログラムイベントのみを処理し、それらを無視する方がはるかに簡単です。

たとえば、この種のコードは機能します。

// Element that needs to be scrolled
var myElement = document.getElementById('my-container');

// Flag to tell if the change was programmatic or by the user
var ignoreNextScrollEvent = false;

function setScrollTop(scrollTop) {
    ignoreNextScrollEvent = true;
    myElement.scrollTop = scrollTop
}

myElement.addEventListener('scroll', function() {
    if (ignoreNextScrollEvent) {
        // Ignore this event because it was done programmatically
        ignoreNextScrollEvent = false;
        return;
    }

    // Process user-initiated event here
});

次に、を呼び出すsetScrollTop()と、スクロールイベントは無視されますが、ユーザーがマウス、キーボード、またはその他の方法でスクロールすると、イベントが処理されます。


2
これは、ブラウザによってトリガーされたスクロールイベントを検出/無視しません。
mfluehr

これは、スクロール動作が「スムーズ」に設定することができることを考慮していません-追加する必要があり、私は似たようなの考えてきた
カミルBęben

3

はい、100%可能です。私は現在、IEが要件ではないアプリケーション(クライアント向けのみ)でこれを使用しています。バックボーンアプリがスクロールが変更されるアニメーションを開始すると、スクロールは発生しますが、これらのイベントキャプチャはトリガーされません。これは、最新のFF、Safari、Chromeでテストされています。

$('html, body').bind('scroll mousedown wheel DOMMouseScroll mousewheel keyup', function(evt) {
  // detect only user initiated, not by an .animate though
    if (evt.type === 'DOMMouseScroll' || evt.type === 'keyup' || evt.type === 'mousewheel') {
    // up
    if (evt.originalEvent.detail < 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta > 0)) { 
    // down.
    } else if (evt.originalEvent.detail > 0 || (evt.originalEvent.wheelDelta && evt.originalEvent.wheelDelta < 0)) { 
  }
}
}); 

1
提案をありがとう。ただし、これはバイナリソリューションです。JavaScriptで開始されたスクロールと非jsスクロールを検出します。私は本当に三元的な解決策が必要です。(javascript、ユーザー、ブラウザー)。
mrtsherman 2014

2

代わりに、MousewheelイベントとDOMMouseScrollイベントを使用してみてください。http://www.quirksmode.org/dom/events/scroll.htmlを参照してください


この提案をありがとう。しかし、私はこれが私が望むものを持っているとは思いません。私は実際にブラウザとユーザースクロールを区別する必要があります。マウスホイールをターゲットにするだけではありません。
mrtsherman 2011

マウスホイールイベントは、onscrollイベントの前に発生すると思います。したがって、mousewheelでvar userscroll = trueを設定し、onscrollでそれを検出できます(そしてfalseにリセットします)。
Gerben 2011

3
ただし、カバーされないユーザー開始のスクロールイベントが多数あります。矢印キー、ウィンドウのサイズ変更など。このイベントの送信者が「X」であると言う方が、このイベント以外のすべてが「X」である必要があると言うよりもはるかに安全です。また、ユーザーではなくブラウザによって開始されたスクロールを識別したいので、それは私にとっては機能しません。したがって、このユースケースを作成するには、すべてのマウスホイールスクロールを追跡し、それに基づいて後続のスクロールイベントがユーザーであるかどうかを推測する必要があります。私はそれがただ実行不可能であると思います。
mrtsherman 2011

2

準備完了時にスクロール位置を確認できます。on scrollイベントを発生させるときは、スクロール位置がページがロードされたときと異なることを確認してください。最後に、ページをスクロールしたら、保存されている値を必ずクリアしてください。

$(function () {
    var loadScrollTop = ($(document).scrollTop() > 0 ? $(document).scrollTop() : null);
    $(document).scroll(function (e) {
        if ( $(document).scrollTop() !== loadScrollTop) {
            // scroll code here!
        }
        loadScrollTop = null;
    });
});

このアイデアをありがとう。これが私にとって完全にカバーしていないいくつかのシナリオがあります。それは良い考えですが、必要に応じてバックポケットに入れておきます。
mrtsherman 2011

シンプルでほとんどの場合効果的です。
アーロン

0

に関して:

具体的には、戻るボタンを使用すると、ブラウザは最後の既知のスクロール位置にジャンプする場合があります。

これは、ページがレンダリングされた後すぐに発生します。スクロールイベントのリッスンを1秒ほど遅らせることができます。


2
これは堅牢なソリューションではありません。ジャンプする時間は、ページの読み込み時間によって異なります。ブラウザは、ページが完全に読み込まれるまでジャンプを遅らせます。これにより、ページの高さ(画像など)を読み込んで変更するページ要素がスクロールして表示されなくなるのを防ぎます。
mrtsherman 2014

0

ユーザーが作成したスクロールを分離するもう1つの方法があります。たとえば、「mousewheel」、「touchmove」、「keydown」などの代替イベントハンドラーを使用して、矢印のスクロール、スクロールバーでのスクロールにコード38と40を指定できます。 「scroll」イベントは、「mouseup」イベントまで「mousedown」と同時に発生します。


-1

これは非常に便利でした。これは、そんなに傾倒している人のためのコーヒースクリプトバージョンです。

$ ->
  S.userScroll = false

  # reset flag on back/forward
  if $.history?
    $.history.init (hash) ->
      S.userScroll = false

  mouseEvent = (e)->
    S.userScroll = true

  $(document).keydown (e) ->
    importantKey =  (e.which == 33 or        # page up
      e.which == 34 or    # page dn
      e.which == 32 or    # spacebar
      e.which == 38 or    # up
      e.which == 40 or    # down
      (e.ctrlKey and e.which == 36) or    # ctrl + home
      (e.ctrlKey and e.which == 35)    # ctrl + end
    )
    if importantKey
      S.userScroll = true;

  # Detect user scroll through mouse
  # Mozilla/Webkit
  if window.addEventListener
      document.addEventListener('DOMMouseScroll', mouseEvent, false);

  # for IE/OPERA etc
  document.onmousewheel = mouseEvent;

-1

より良い答えがあるよりもJQueryを使用している場合は、どうやら-私はそれを自分で試しているだけです。

参照:ユーザーによるjqueryイベントトリガーの検出またはコードによる呼び出し


提案をありがとう。ただし、これはバイナリソリューションです。JavaScriptで開始されたスクロールと非jsスクロールを検出します。私は本当に三元的な解決策が必要です。(javascript、ユーザー、ブラウザー)。ちなみに、jQueryは提案されたソリューションの要件ではありません。
mrtsherman 2014

-1

アプリケーションには役立たないかもしれませんが、ユーザースクロールでイベントを発生させる必要がありましたが、他の人に役立つ場合に備えて、プログラムによるスクロールと投稿は必要ありませんでした。

wheel代わりにイベントを聞くscroll

これは、ユーザーがマウスホイールまたはトラッカーパッド(とにかくほとんどの人がスクロールする方法だと思います)を使用するたびにトリガーされ、プログラムでスクロールしても起動されません。ユーザーのスクロールアクションとスクロール機能を区別するために使用しました。

https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event


element.addEventListener('wheel', (event) => {
       //do user scroll stuff here
})

注意点の1つは、wheelモバイルのスクロールでは起動しないため、デバイスがモバイルであり、同様の機能を使用しているかどうかを確認しました

if(this.mobile){
  element.addEventListener('scroll', (event) => {
       //do mobile scroll stuff here
  })
}

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