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

Detect whether scroll event was created by user

本文关键字:用户 创建 是否 滚动 事件 检测      更新时间:2023-09-26

是否有可能判断滚动事件是由浏览器还是由用户完成的?具体来说,当使用后退按钮时,浏览器可能会跳转到最后一个已知的滚动位置。如果我绑定到滚动事件,我怎么知道这是由用户还是浏览器引起的?

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

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

  1. 用户执行操作。例如,使用鼠标滚轮、方向键、上下翻页键、home/end键,点击滚动条或拖动拇指。
  2. 浏览器自动滚动。例如,当你在浏览器中使用后退按钮时,它会自动跳转到最后一个已知的滚动位置。
  3. Javascript卷轴。例如:element.scrollTo(x,y) .

不幸的是,没有直接的方法可以告诉你。

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

如果没有,我能想到的一个解决方法是跟踪用户发起的滚动,并检查滚动是由浏览器还是由用户触发的。

这里有一个我放在一起的例子,它做得很好(除了那些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可能因操作系统而异,因此您可能必须适当更改。

希望这对你有帮助!

与其试图捕捉所有用户事件,还不如相反,只处理编程事件并忽略它们。

例如,这样的代码可以工作:

// 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()时,滚动事件将被忽略,而如果用户使用鼠标、键盘或任何其他方式滚动,则该事件将被处理。

据我所知,不可能(没有任何工作)告诉滚动事件是何时由"user"或其他方式发出的。

你可以尝试(正如其他人提到的)捕捉鼠标滚轮事件,然后可能尝试捕捉keydown事件上的任何键,可以触发滚动(箭头,空格等),同时检查什么元素是当前聚焦的,因为你不能使用箭头键滚动,而在输入字段中输入。一般来说,这将是一个复杂而混乱的脚本。

根据你正在处理的情况,我猜你可以"恢复逻辑",而不是检测用户发出的滚动事件,只是挂钩到任何以编程方式制作的滚动,并处理任何滚动事件而不是由你的代码作为由用户制作。就像我说的,这取决于具体情况,以及你想要达到的目标。

是的,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)) { 
  }
}
}); 

尝试使用Mousewheel和dommousescroroll事件代替。参见http://www.quirksmode.org/dom/events/scroll.html

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

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

关于:

具体来说,当使用后退按钮时,浏览器可能会跳转到最后一个已知的滚动位置。

在页面呈现后很快触发。您可以将侦听滚动事件的时间延迟1秒左右。

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

发现这个非常有用。这里有一个coffeescript版本给那些有兴趣的人。

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

如果您正在使用JQuery,那么显然有一个更好的答案-我只是自己尝试一下。

参见:检测jquery事件触发用户或代码调用

这可能对您的应用程序没有帮助,但我需要在用户滚动上触发一个事件,而不是程序性滚动和张贴,以防它帮助其他人。

监听wheel事件而不是scroll

当用户使用鼠标滚轮或跟踪板时(我觉得大多数人都是这样滚动的),它就会被触发,当你编程滚动时,它不会被触发。我用它来区分用户滚动操作和滚动功能。

https://developer.mozilla.org/en-US/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
  })
}