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

IT技术 javascript jquery
2021-01-25 08:02:37

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

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

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

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

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

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

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

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

希望这可以帮助!

使用 home 键和 end 键滚动时,您不必按下 control。
2021-03-21 08:02:37
DOMMouseScroll 不适用于 Mac 上最新的 Chrome。最好使用 document.addEventListener('mousewheel' 而不是设置 onmousewheel
2021-03-22 08:02:37
谢谢先生。我认为这是最好的答案,即使它不是我希望的那样(什么?没有event.thrower财产?!)。
2021-04-05 08:02:37

与其试图捕获所有用户事件,不如反其道而行之,只处理程序化事件——并忽略它们。

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

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

这不会检测/忽略浏览器触发的滚动事件。
2021-03-21 08:02:37
我一直在考虑必须添加类似的东西 - 这并没有考虑到滚动行为可以设置为“平滑”
2021-04-04 08:02:37

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

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

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

没关系。这不是关于赏金,而是关于传播和获得知识:)
2021-03-19 08:02:37
谢谢WTK。这是一段时间以来的最佳答案,但不幸的是,Mrchief 用一些代码示例阐述了您的答案,所以我给了他赏金。如果我能把它分开,我会的!
2021-04-07 08:02:37

是的,这是 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,用户,浏览器)。
2021-03-20 08:02:37

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

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