【问题标题】:Detect whether scroll event was created by user检测用户是否创建了滚动事件
【发布时间】:2011-10-25 12:57:57
【问题描述】:

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

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

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

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

【问题讨论】:

  • 我不确定你的问题,如果你认为使用后退按钮跳转到我是浏览器或用户的滚动事件。一般来说:您认为“通过浏览器滚动”是什么?如果您的意思是由脚本启动滚动,那么您需要做的就是在脚本滚动时停用事件处理程序或设置一个标志,以便事件处理程序知道忽略它。
  • 我认为通过后退按钮滚动是一种“浏览器滚动”。其他任何东西——鼠标滚轮、上/下箭头、中心按钮点击等都是用户滚动。我想我真正的问题可能是 - 有没有办法区分事件的来源?我确实查看了事件对象的属性,但找不到任何东西。我可以想象的三种场景是浏览器启动滚动、javascript启动滚动和用户启动滚动。希望这能让事情更清楚。
  • @mrtsherman 我发现了其中一些,同时实现了相同的输出:stackoverflow.com/questions/2834667/…

标签: javascript jquery


【解决方案1】:

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

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

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

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

希望这会有所帮助!

【讨论】:

  • 谢谢先生。我认为这是最好的答案,即使它不是我希望的那样(什么?没有event.thrower 属性?!)。
  • 使用 home 键和 end 键滚动时不必按控制键。
  • DOMMouseScroll 在 Mac 上的最新 Chrome 上不起作用。最好使用 document.addEventListener('mousewheel' 而不是设置 onmousewheel
【解决方案2】:

与其试图捕获所有用户事件,不如反其道而行之,只处理程序化事件,而忽略这些事件要容易得多。

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

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

【讨论】:

  • 这不会检测/忽略浏览器触发的滚动事件。
  • 我一直在考虑必须添加类似的东西 - 这没有考虑到滚动行为可以设置为“平滑”
【解决方案3】:

据我所知,无论何时“用户”或通过其他方式发出滚动事件,都无法判断(无需任何工作)。

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

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

【讨论】:

  • 感谢 WTK。这是一段时间内最好的答案,但不幸的是,Mrchief 用一些代码示例解释了你的答案,所以我给了他赏金。如果我能把它分开,我会的!
  • 没关系。这不是关于赏金,而是关于传播和获取知识:)
【解决方案4】:

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

【讨论】:

  • 感谢您的建议。但是,这是一个二进制解决方案 - 检测 javascript 启动的滚动与非 js 滚动。我真的需要一个三元解决方案。 (javascript、用户、浏览器)。
【解决方案5】:

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

【讨论】:

  • 感谢您的建议。但是我不认为这有我想要的。我实际上需要区分浏览器和用户滚动。不只是针对鼠标滚轮。
  • 我认为 mousewheel 事件是在 onscroll 事件之前触发的。因此,您可以在鼠标滚轮上设置 var userscroll=true,并在 onscroll 中检测它(并将其重置为 false)。
  • 有大量的用户发起的滚动事件,但不会被覆盖。箭头键、窗口大小调整等。说这个事件的发送者是“X”比说这个事件之外的所有东西都必须是“X”要安全得多。它也对我不起作用,因为我想识别由浏览器而不是用户发起的滚动。因此,要制作这个用例,我必须跟踪所有鼠标滚轮滚动,然后尝试推断后续滚动事件是否是基于此的用户。恐怕只是行不通。
  • 滚轮事件不一定会触发滚动事件,具体取决于上下文。 developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
【解决方案6】:

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

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

【讨论】:

  • 感谢您提出这个想法。在某些情况下,这对我来说并没有完全涵盖。虽然这是个好主意,如果我需要的话,我会把它放在我的后口袋里。
  • 简单且在大多数情况下有效。
【解决方案7】:

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

【讨论】:

  • 有更多的情况可能会导致滚动,例如,您尝试在当前屏幕之前的滚动元素中插入一些内容。
【解决方案8】:

关于:

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

在页面渲染后很快就会触发。您可以将滚动事件的侦听延迟 1 秒左右。

【讨论】:

  • 这不是一个可靠的解决方案。跳转时间取决于页面加载时间。浏览器延迟跳转,直到页面完全加载。这可以防止加载和更改页面高度(如图像)的页面元素滚动到视图之外。
【解决方案9】:

发现这非常有用。这是一个咖啡脚本版本,适合那些有兴趣的人。

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

【讨论】:

    【解决方案10】:

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

    见:Detect jquery event trigger by user or call by code

    【讨论】:

    • 感谢您的建议。但是,这是一个二进制解决方案 - 检测 javascript 启动的滚动与非 js 滚动。我真的需要一个三元解决方案。 (javascript、用户、浏览器)。旁注,jQuery 不是您提出的解决方案的要求。
    【解决方案11】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-02
      • 2015-01-18
      • 1970-01-01
      • 1970-01-01
      • 2011-09-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多