Javascript 检测滚动事件是否由用户创建
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7035896/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Detect whether scroll event was created by user
提问by mrtsherman
Is it possible to tell whether a scroll event was done by the browser or by the user? Specifically, when using the back button a browser may jump to the last known scroll position. If I bind to scroll event how can I tell whether this was caused by user or browser?
是否可以判断滚动事件是由浏览器还是由用户完成?具体来说,当使用后退按钮时,浏览器可能会跳转到最后一个已知的滚动位置。如果我绑定到滚动事件,我怎么知道这是由用户还是浏览器引起的?
$(document).scroll( function(){
//who did this?!
});
I see three types of situations that cause scrolling in a browser.
我看到了三种导致浏览器滚动的情况。
- The user performs some action. For example, uses mousewheel, arrow keys, page up/down keys, home/end keys.
- The browser scrolls automatically. For example, when using the back button in your browser it will jump to the last known scroll position automatically.
- Javascript scrolls. For example,
element.scrollTo(x,y)
.
- 用户执行某些操作。例如,使用鼠标滚轮、箭头键、向上/向下翻页键、主页/结束键。
- 浏览器会自动滚动。例如,在浏览器中使用后退按钮时,它会自动跳转到最后一个已知的滚动位置。
- Javascript 滚动。例如,
element.scrollTo(x,y)
。
采纳答案by Mrchief
Unfortunately, there is no direct way of telling that.
不幸的是,没有直接的方法可以说明这一点。
I would say if you can redesignyour app so that it doesn't depend on this type of flow, go for that.
我会说,如果您可以重新设计您的应用程序,使其不依赖于这种类型的流程,那就去做吧。
If not, a workaroundI can think of is to keep track of user initiated scrolls and check that to see if the scroll was triggered by the browser or by the user.
如果没有,我能想到的解决方法是跟踪用户启动的滚动并检查滚动是由浏览器还是由用户触发。
Here's an example that I put together which does this pretty well (except for browsers where jQuery history has problems with).
这是我放在一起的一个例子,它做得很好(除了 jQuery 历史有问题的浏览器)。
You need to run this locally to be able to test it fully (jsFiddle/jsbin are not good fits as they iFrame the contents).
您需要在本地运行它才能对其进行全面测试(jsFiddle/jsbin 不适合,因为它们 iFrame 内容)。
Here's the test casesthat I validated:
这是我验证的测试用例:
- Page loads -
userScroll
isfalse
- Scroll using mouse/keyboard -
userScroll
becomestrue
- Click on the link to jump to page bottom -
userScroll
becomesfalse
- Click Back/Forward -
userScroll
becomesfalse
;
- 页面加载 -
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>
Notes:
笔记:
- This doesn't track scrolling when the user drags the scrollbar with mouse. This can be added with some more code, which I left as an exercise for you.
event.keyCodes
may vary by OS, so you may have to change that appropriately.
- 当用户用鼠标拖动滚动条时,这不会跟踪滚动。这可以添加一些更多的代码,我留给你作为练习。
event.keyCodes
可能因操作系统而异,因此您可能需要适当地更改它。
Hope this helps!
希望这可以帮助!
回答by WTK
As far as I know it is impossible (without any work) to tell whenever scroll event has been issued by "user" or by other means.
据我所知,不可能(没有任何工作)告诉“用户”或其他方式何时发出滚动事件。
You could try (as others mentioned) catch mousewheel events, then probably trying to catch keydown event on any keys that can trigger scroll (arrows, space etc.) while checking what element is currently focused, since you for example can't scroll using arrow keys while typing in an input field. In general that would be complex and messy script.
您可以尝试(正如其他人提到的)捕获 mousewheel 事件,然后可能尝试在检查当前聚焦的元素时在任何可以触发滚动(箭头、空格等)的键上捕获 keydown 事件,因为例如您无法滚动使用在输入字段中键入时使用箭头键。一般来说,这将是复杂而凌乱的脚本。
Depending on situation you're dealing with you could I guess "revert the logic", and instead of detecting user issued scroll events just hook in into any scrolls made programatically and treat any scroll events notmade by your code as made by an user. Like I said it depends on a situation, and what you're trying to achive.
根据您正在处理的情况,我可以猜测“恢复逻辑”,而不是检测用户发出的滚动事件,只需挂入以编程方式制作的任何滚动中,并将任何不是由您的代码制作的滚动事件视为由用户制作。就像我说的,这取决于情况,以及您想要达到的目标。
回答by laurent
Rather than trying to catch all the user events, it's much easier to do the opposite and handle only the programmatic events - and ignore those.
与其试图捕获所有用户事件,不如反其道而行之,只处理程序化事件——并忽略这些事件。
For example, this kind of code would work:
例如,这种代码可以工作:
// 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
});
Then when you call setScrollTop()
, the scroll event will be ignored, while if the user scroll with the mouse, keyboard or any other way, the event will be processed.
然后当您调用 时setScrollTop()
,滚动事件将被忽略,而如果用户使用鼠标、键盘或任何其他方式滚动,则将处理该事件。
回答by laurent
Yes, it is 100% possible. I'm current using this in an application where IE is not a requirement - client facing only. When my Backbone app initiates an animation where scroll is changed - scroll occurs but does not trigger these event captures. This is tested in FF, Safari & Chrome latest.
是的,这是 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)) {
}
}
});
回答by Gerben
Try using the Mousewheel and DOMMouseScroll events instead. See http://www.quirksmode.org/dom/events/scroll.html
尝试改用 Mousewheel 和 DOMMouseScroll 事件。见http://www.quirksmode.org/dom/events/scroll.html
回答by Rusty Jeans
You can check the scroll position on ready. When you fire the on scroll event check to make sure the scroll position is different than it was when the page loaded. Lastly be sure to clear out the stored value once the page is scrolled.
您可以在准备好时检查滚动位置。当您触发 on scroll 事件检查以确保滚动位置与页面加载时不同。最后一定要在页面滚动后清除存储的值。
$(function () {
var loadScrollTop = ($(document).scrollTop() > 0 ? $(document).scrollTop() : null);
$(document).scroll(function (e) {
if ( $(document).scrollTop() !== loadScrollTop) {
// scroll code here!
}
loadScrollTop = null;
});
});
回答by Firsh - LetsWP.io
Regarding to:
关于:
Specifically, when using the back button a browser may jump to the last known scroll position.
具体来说,当使用后退按钮时,浏览器可能会跳转到最后一个已知的滚动位置。
That fires very soon, after the page is rendered. You can just delay listenting to the scroll event by 1 second or so.
在页面呈现后,这很快就会触发。您可以将滚动事件的侦听延迟 1 秒左右。
回答by Alex Kuzmin
There is one more way to separate the user-created scroll: you can use the alternative event handlers, for example 'mousewheel', 'touchmove', 'keydown' with codes 38 and 40 for arrow scrolling, for scrolling with scroll bar - if 'scroll' event is fired simultaneously with 'mousedown' until 'mouseup' event.
还有另一种方法来分离用户创建的滚动:您可以使用替代事件处理程序,例如 'mousewheel'、'touchmove'、'keydown',代码 38 和 40 用于箭头滚动,用于滚动条滚动 - 如果'scroll' 事件与 'mousedown' 同时触发,直到 'mouseup' 事件。
回答by TimothyBuktu
It might not help with your application, but I needed to fire an event on user scroll but not programatic scroll and posting incase it helps anyone else.
它可能对您的应用程序没有帮助,但我需要在用户滚动上触发一个事件,而不是编程滚动和发布,以防它帮助其他人。
Listen to the wheel
event instead of scroll
,
监听wheel
事件而不是scroll
,
It is triggered whenever a user uses the mouse wheel or tracker pad( which I feel is how most people scroll anyway) and isn't fired when you programatically scroll. I used it to differentiate between a user scroll action and scrolling functions.
每当用户使用鼠标滚轮或跟踪器板(我觉得大多数人都是这样滚动)时,它就会被触发,并且在您以编程方式滚动时不会被触发。我用它来区分用户滚动操作和滚动功能。
https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
element.addEventListener('wheel', (event) => {
//do user scroll stuff here
})
One caveat is that wheel
doesn't fire on scroll on mobile, so I checked whether the device was mobile and used similar functions
一个警告是wheel
在移动设备上滚动时不会触发,所以我检查了设备是否是移动设备并使用了类似的功能
if(this.mobile){
element.addEventListener('scroll', (event) => {
//do mobile scroll stuff here
})
}
回答by nathan g
if you're using JQuery than there's a better answer, apparently - i'm just trying it out myself.
如果您使用的是 JQuery,那么显然有更好的答案 - 我只是自己尝试一下。