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

时间:2011-08-12 05:23:27

标签: javascript jquery

是否可以判断滚动事件是由浏览器还是由用户完成的?具体地,当使用后退按钮时,浏览器可以跳转到最后已知的滚动位置。如果我绑定滚动事件,我如何判断这是由用户还是浏览器引起的?

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

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

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

11 个答案:

答案 0 :(得分:23)

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

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

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

这是我放在一起的一个例子,它做得非常好(除了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可能会因操作系统而异,因此您可能需要对其进行适当更改。

希望这有帮助!

答案 1 :(得分:5)

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

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

根据您正在处理的情况,我可以猜测“恢复逻辑”,而不是检测用户发布的滚动事件,而只是挂钩到以编程方式创建的任何滚动并处理任何滚动事件由您的代码根据用户制作。 就像我说的那样取决于一种情况,以及你想要实现的目标。

答案 2 :(得分:3)

是的,这是100%可能的。我目前在一个不需要IE的应用程序中使用它 - 仅面向客户端。当我的Backbone应用程序启动滚动更改的动画时 - 滚动发生但不会触发这些事件捕获。这是在FF,Safari和amp; 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)) { 
  }
}
}); 

答案 3 :(得分:3)

不是试图捕获所有用户事件,而是更容易做相反的事情而只处理程序化事件 - 而忽略那些事件。

例如,这种代码可以工作:

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

答案 4 :(得分:2)

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

答案 5 :(得分:1)

您可以在准备好时检查滚动位置。当您触发滚动事件检查以确保滚动位置与加载页面时的滚动位置不同。最后一定要在页面滚动后清除存储的值。

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

答案 6 :(得分:0)

发现这非常有用。对于那些如此倾向的人来说,这是一个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;

答案 7 :(得分:0)

如果你使用的是JQuery而不是一个更好的答案,显然 - 我只是自己尝试一下。

请参阅:Detect jquery event trigger by user or call by code

答案 8 :(得分:0)

关于:

  

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

在页面渲染后很快就会发生火灾。你可以将滚动事件的持续时间延迟1秒左右。

答案 9 :(得分:0)

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

答案 10 :(得分:-1)

这可能对您的应用程序无济于事,但我需要在用户滚动时触发一个事件,但在程序化滚动时不触发并发布一个事件,以防它对其他人有帮助。

收听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
  })
}