检测滚动事件是否由用户创建


82

是否可以判断滚动事件是由浏览器还是由用户完成的?具体而言,当使用“后退”按钮时,浏览器可能会跳至上一个已知的滚动位置。如果绑定滚动事件,如何分辨这是由用户还是浏览器引起的?

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

我看到三种导致浏览器滚动的情况。

  1. 用户执行一些操作。例如,使用鼠标滚轮,箭头键,上/下翻页键,主页/结束键。
  2. 浏览器将自动滚动。例如,在浏览器中使用后退按钮时,它将自动跳至上一个已知的滚动位置。
  3. Javascript滚动。例如,element.scrollTo(x,y)

1
从您的问题中我不确定,如果您考虑使用后botton跳转到我的浏览器或用户的滚动事件。通常:您认为“浏览器滚动”是什么?如果您是说由脚本启动滚动,那么您要做的就是在脚本滚动时停用事件处理程序或设置一个标志,以便事件处理程序知道忽略它。
RoToRa 2011年

我认为通过“后退”按钮滚动是“浏览器滚动”。其他任何事情-鼠标滚轮,上/下箭头,中心按钮单击等都是用户滚动。我想我的真正问题可能是-是否有任何方法可以区分事件的来源?我确实查看了事件对象的属性,但是找不到任何东西。我可以想象的三种情况是浏览器启动的滚动,javascript启动的滚动和用户启动的滚动。希望事情变得更清楚。
mrtsherman 2011年

@mrtsherman我在实现相同输出的同时发现了其中一些: stackoverflow.com/questions/2834667/…–
AlphaMale

Answers:


30

不幸的是,没有直接的方式可以说明这一点。

我想说的是,如果您可以重新设计应用程序以使其不依赖于这种类型的流程,那就去吧。

如果没有,我可以想到的解决方法是跟踪用户启动的滚动并检查滚动是否由浏览器或用户触发。

这是我整理好的一个示例(jQuery历史记录有问题的浏览器除外)。

您需要在本地运行此程序,以便能够对其进行全面测试(jsFiddle / jsbin不适用于iFrame内容)。

这是我验证的测试用例

  • 页面加载-userScrollfalse
  • 使用鼠标/键盘滚动-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 可能因操作系统而异,因此您可能必须适当地进行更改。

希望这可以帮助!


谢谢先生。我认为这是最好的答案,即使这不是我希望的(什么?没有event.thrower财产?!)。
mrtsherman 2011年

使用主页键和结束键滚动时,无需按控制键。
柯蒂斯

DOMMouseScroll在Mac上最新的Chrome上不起作用。最好使用document.addEventListener('mousewheel',而不要设置onmousewheel
Curtis

8

据我所知,不可能(无任何工作)告诉“用户”或其他方式何时发出滚动事件。

您可以尝试(如其他人所提到的那样)捕获鼠标滚轮事件,然后可能尝试在检查当前聚焦的元素时捕获任何可以触发滚动的键按下事件(箭头,空格等),因为例如您无法使用在输入字段中键入箭头键。通常,这将是复杂且混乱的脚本。

根据您要处理的情况,我可能会猜测为“还原逻辑”,而不是检测用户发出的滚动事件,只需挂接到以编程方式进行的所有滚动中,并将由代码进行的滚动事件视为由用户进行。就像我说的,这取决于情况以及您要达到的目标。


感谢WTK。这是一段时间以来最好的答案,但是不幸的是,Mrchief用一些代码示例详细说明了您的答案,所以我将赏金给予了他。如果我可以拆分,我将拥有!
mrtsherman 2011年

4
没关系。这不是关于赏金,而是关于传播和获取知识:)
WTK

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

我一直在考虑必须添加类似的内容-这没有考虑到可以将滚动行为设置为“平滑”
KamilBęben

3

是的,这是100%可能的。我当前在不要求IE的应用程序中使用它-仅面向客户端。当我的Backbone应用程序启动动画并在其中更改了滚动时-发生滚动但不会触发这些事件捕获。最新在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

3
尽管有很多用户启动的滚动事件,但这些事件不会涉及。箭头键,窗口调整大小等。说这个事件的发送者是“ X”比说这个事件之外的所有东西都必须是“ X”要安全得多。无论如何,它都不适合我,因为我想识别由浏览器而不是用户启动的滚动。因此,要成为该用例,我将必须跟踪所有鼠标滚轮滚动,然后尝试基于此推论随后的滚动事件是否是用户。恐怕这是行不通的。
mrtsherman 2011年

2

您可以随时检查滚动位置。当您触发滚动事件时,请确保滚动位置与加载页面时的滚动位置不同。最后,请确保在滚动页面后清除存储的值。

$(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

还有一种分离用户创建的滚动的方法:您可以使用其他事件处理程序,例如带有代码38和40的'mousewheel','touchmove','keydown',用于箭头滚动,用于使用滚动条滚动-如果“ scroll”事件与“ mousedown”同时触发,直到“ mouseup”事件。


-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/zh-CN/docs/Web/API/Element/wheel_event


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

一个警告是wheel在移动设备上滚动时不会触发,因此我检查了该设备是否为移动设备并使用了相似的功能

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.