如何确定jQuery滚动事件的方向?

375

我正在寻找类似于这种效果的东西:

$(window).scroll(function(event){
   if (/* magic code*/ ){
       // upscroll code
   } else {
      // downscroll code
   }
});

有什么想法吗?


对于那些在弹性滚动方面遇到问题的人,请使用此答案 https://dev59.com/7Ww05IYBdhLWcg3wkivX - Timothy Dalton
1
现在最容易使用的是wheel事件:https://dev59.com/7Ww05IYBdhLWcg3wkivX#33334461。 - Shikkediel
25个回答

755

检查当前的scrollTop和先前的scrollTop之间的差异

var lastScrollTop = 0;
$(window).scroll(function(event){
   var st = $(this).scrollTop();
   if (st > lastScrollTop){
       // downscroll code
   } else {
      // upscroll code
   }
   lastScrollTop = st;
});

17
有没有设置灵敏度的方法? - cocoa coder
10
我在这个代码片段中提供了一个示例。由于它很受欢迎,也许我会更新这个答案并创建一个jQuery插件。 - Josiah Ruddell
3
如果你回到之前有这个代码的页面,你是否尝试过这个方法?如果你将页面滚动到下方,比如说滚动了500像素,然后跳转到另一个页面,再回到之前的页面。一些浏览器会保留之前滚动的位置,让你回到页面下方。那么你的起始滚动位置 lastScrollTop 是0还是会被正确初始化? - TCHdvlp
7
在最坏的情况下,第一次滚动事件会更新变量,而第二个事件将是准确的(只有在返回导航后向上滚动才会产生影响)。这可以通过在页面加载后浏览器更新滚动位置后设置 var lastScrollTop = $(window).scrollTop() 来解决。请注意不改变原意,使语言更加通俗易懂。 - Josiah Ruddell
2
关于此事的更新:请注意,某些浏览器,特别是Windows 8上的IE 11,可能会以亚像素为基础(平滑滚动)触发滚动事件。但由于它将scrollTop报告为整数,因此您先前的滚动值可能与当前值相同。 - Renato
显示剩余13条评论

181

你可以不需要追踪之前的滚动位置就能实现此功能,而所有其他示例都需要:

$(window).bind('mousewheel', function(event) {
    if (event.originalEvent.wheelDelta >= 0) {
        console.log('Scroll up');
    }
    else {
        console.log('Scroll down');
    }
});

我不是这方面的专家,所以请自行进一步研究,但似乎当您使用 $(element).scroll 时,监听的事件是“scroll”事件。

但如果您通过使用bind来特别监听一个mousewheel事件,则回调参数的originalEvent属性包含不同的信息。 其中一部分信息是wheelDelta。 如果它是正数,则表示您向上移动鼠标滚轮。 如果它是负数,则表示您向下移动鼠标滚轮。

我猜mousewheel事件将在鼠标滚轮转动时触发,即使页面不滚动; 这种情况下可能不会触发“scroll”事件。 如果您希望如此,您可以在回调底部调用event.preventDefault()以防止页面滚动,并且可以使用鼠标滚轮事件来执行除页面滚动之外的其他功能,例如某种缩放功能。


13
不错,但遗憾的是唯一的Firefox不支持它 https://developer.mozilla.org/en-US/docs/DOM/DOM_event_reference/mousewheel (在FF v15中测试过)。 :C - nuala
8
对我很有用:我需要检测滚动,即使页面本身实际上并没有滚动,因此 scrollTop 没有更新。事实上, $(window).scroll() 根本没有触发。 - Martti Laine
14
很好,你不需要旧状态。然而,这种技术在移动设备或平板电脑上无法使用,因为它们没有鼠标滚轮! - joeytwiddle
13
如果我使用键盘滚动,这种方法就行不通了。对于像缩放这样的事情,这绝对是一个有趣的方法,但我认为它并没有解决滚动方向的问题。 - chmac
6
$("html, body").bind({'mousewheel DOMMouseScroll onmousewheel touchmove scroll': function(e) { //... }); 对于我来说可以检测到所有浏览器滚动数据。 - GoreDefex
显示剩余8条评论

34

现有解决方案

这篇文章提供了3种解决方案其他答案

解决方案1

    var lastScrollTop = 0;
    $(window).on('scroll', function() {
        st = $(this).scrollTop();
        if(st < lastScrollTop) {
            console.log('up 1');
        }
        else {
            console.log('down 1');
        }
        lastScrollTop = st;
    });

解决方案2

    $('body').on('DOMMouseScroll', function(e){
        if(e.originalEvent.detail < 0) {
            console.log('up 2');
        }
        else {
            console.log('down 2');
        }
    });

解决方案3
    $('body').on('mousewheel', function(e){
        if(e.originalEvent.wheelDelta > 0) {
            console.log('up 3');
        }
        else {
            console.log('down 3');
        }
    });

多浏览器测试

我无法在Safari上进行测试

chrome 42 (Win 7)

  • 解决方案1
    • 向上滚动:每次滚动1个事件
    • 向下滚动:每次滚动1个事件
  • 解决方案2
    • 向上滚动:不起作用
    • 向下滚动:不起作用
  • 解决方案3
    • 向上滚动:每次滚动1个事件
    • 向下滚动:每次滚动1个事件

Firefox 37 (Win 7)

  • 解决方案1
    • 向上滚动:每次滚动20个事件
    • 向下滚动:每次滚动20个事件
  • 解决方案2
    • 向上滚动:不起作用
    • 向下滚动:每次滚动1个事件
  • 解决方案3
    • 向上滚动:不起作用
    • 向下滚动:不起作用

IE 11 (Win 8)

  • 解决方案1
    • 向上滚动:每滚动1次触发10个事件(副作用:最后会触发一次向下滚动事件)
    • 向下滚动:每滚动1次触发10个事件
  • 解决方案2
    • 向上滚动:不可用
    • 向下滚动:不可用
  • 解决方案3
    • 向上滚动:不可用
    • 向下滚动:每滚动1次触发1个事件

IE 10(Win 7)

  • 解决方案1
    • 向上滚动:每滚动1次触发1个事件
    • 向下滚动:每滚动1次触发1个事件
  • 解决方案2
    • 向上滚动:不可用
    • 向下滚动:不可用
  • 解决方案3
    • 向上滚动:每滚动1次触发1个事件
    • 向下滚动:每滚动1次触发1个事件

IE 9(Win 7)

  • 解决方案1
    • 向上滚动:每滚动1次触发1次事件
    • 向下滚动:每滚动1次触发1次事件
  • 解决方案2
    • 向上滚动:不可用
    • 向下滚动:不可用
  • 解决方案3
    • 向上滚动:每滚动1次触发1次事件
    • 向下滚动:每滚动1次触发1次事件

IE 8 (Win 7)

  • 解决方案1
    • 向上滚动:每滚动1次触发2次事件(副作用:最后会出现向下滚动)
    • 向下滚动:每滚动1次触发2~4次事件
  • 解决方案2
    • 向上滚动:不可用
    • 向下滚动:不可用
  • 解决方案3
    • 向上滚动:每滚动1次触发1次事件
    • 向下滚动:每滚动1次触发1次事件

综合方案

我检查了IE 11和IE 8的副作用,发现是由于if else语句引起的。因此,我将其替换为以下的if else if语句。

通过多浏览器测试,我决定对于常见浏览器使用解决方案3,对于火狐和IE 11使用解决方案1

我参考了这个答案来检测IE 11。

    // Detect IE version
    var iev=0;
    var ieold = (/MSIE (\d+\.\d+);/.test(navigator.userAgent));
    var trident = !!navigator.userAgent.match(/Trident\/7.0/);
    var rv=navigator.userAgent.indexOf("rv:11.0");

    if (ieold) iev=new Number(RegExp.$1);
    if (navigator.appVersion.indexOf("MSIE 10") != -1) iev=10;
    if (trident&&rv!=-1) iev=11;

    // Firefox or IE 11
    if(typeof InstallTrigger !== 'undefined' || iev == 11) {
        var lastScrollTop = 0;
        $(window).on('scroll', function() {
            st = $(this).scrollTop();
            if(st < lastScrollTop) {
                console.log('Up');
            }
            else if(st > lastScrollTop) {
                console.log('Down');
            }
            lastScrollTop = st;
        });
    }
    // Other browsers
    else {
        $('body').on('mousewheel', function(e){
            if(e.originalEvent.wheelDelta > 0) {
                console.log('Up');
            }
            else if(e.originalEvent.wheelDelta < 0) {
                console.log('Down');
            }
        });
    }

如果有人能够添加Safari浏览器的结果,那将有助于补充解决方案。 - Chemical Programmer
哎呀!我发现移动版Chrome和Android默认浏览器只被解决方案1所覆盖。因此最好同时使用解决方案1和3来覆盖各种浏览器。 - Chemical Programmer
如果设计是固定布局设计,则无法工作。如果您想在静态非滚动网站上检测滚动方向,则Firefox和IE11将无法工作。 - Shannon Hochkins
我发现当我使用这个时,它在Chrome中使用“其他浏览器”,但是当您希望检测鼠标滚轮和滚动条的滚动时,这并没有什么用处。.scroll将检测Chrome中的两种类型的滚动。 - Someone

33

存储先前的滚动位置,然后查看新位置是否大于或小于该位置。

以下是一种避免使用全局变量的方法(这里提供了演示):

(function () {
    var previousScroll = 0;

    $(window).scroll(function(){
       var currentScroll = $(this).scrollTop();
       if (currentScroll > previousScroll){
           alert('down');
       } else {
          alert('up');
       }
       previousScroll = currentScroll;
    });
}()); //run this anonymous function immediately

12

我知道已经有一个被接受的答案,但是我想发表一下我正在使用的内容,以防有人需要。我通过鼠标滚轮事件获取cliphex的方向,但支持Firefox。这种方式很有用,尤其是在像锁定滚动并且无法获取当前滚动高度的情况下。

查看实时版本这里

$(window).on('mousewheel DOMMouseScroll', function (e) {

    var direction = (function () {

        var delta = (e.type === 'DOMMouseScroll' ?
                     e.originalEvent.detail * -40 :
                     e.originalEvent.wheelDelta);

        return delta > 0 ? 0 : 1;
    }());

    if(direction === 1) {
       // scroll down
    }
    if(direction === 0) {
       // scroll up
    }
});

@EtienneMartin 上述代码依赖于jQuery,如果这是导致您的错误的原因,请查看附加的fiddle以查看其工作情况。 - souporserious

8

如果您想知道使用指针设备(鼠标或触摸板)向上滚动还是向下滚动,可以使用wheel事件的deltaY属性。

$('.container').on('wheel', function(event) {
  if (event.originalEvent.deltaY > 0) {
    $('.result').append('Scrolled down!<br>');
  } else {
    $('.result').append('Scrolled up!<br>');
  }
});
.container {
  height: 200px;
  width: 400px;
  margin: 20px;
  border: 1px solid black;
  overflow-y: auto;
}
.content {
  height: 300px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="container">
  <div class="content">
    Scroll me!
  </div>
</div>

<div class="result">
  <p>Action:</p>
</div>


这个链接说不要依赖deltaY。https://developer.mozilla.org/en-US/docs/Web/Events/wheel - Charlie Martin

8

滚动事件

滚动事件在Firefox浏览器中行为比较奇怪(因为平滑滚动的原因会触发很多次),但它是有效的。

注意:实际上,scroll事件在拖动滚动条、使用光标键或鼠标滚轮时会被触发

//creates an element to print the scroll position
$("<p id='test'>").appendTo("body").css({
    padding: "5px 7px",
    background: "#e9e9e9",
    position: "fixed",
    bottom: "15px",
    left: "35px"
});

//binds the "scroll" event
$(window).scroll(function (e) {
    var target = e.currentTarget,
        self = $(target),
        scrollTop = window.pageYOffset || target.scrollTop,
        lastScrollTop = self.data("lastScrollTop") || 0,
        scrollHeight = target.scrollHeight || document.body.scrollHeight,
        scrollText = "";

    if (scrollTop > lastScrollTop) {
        scrollText = "<b>scroll down</b>";
    } else {
        scrollText = "<b>scroll up</b>";
    }

    $("#test").html(scrollText +
      "<br>innerHeight: " + self.innerHeight() +
      "<br>scrollHeight: " + scrollHeight +
      "<br>scrollTop: " + scrollTop +
      "<br>lastScrollTop: " + lastScrollTop);

    if (scrollHeight - scrollTop === self.innerHeight()) {
      console.log("► End of scroll");
    }

    //saves the current scrollTop
    self.data("lastScrollTop", scrollTop);
});

滚轮事件

您也可以查看MDN,它提供了关于滚轮事件的详细信息。

注意:滚轮事件仅在使用鼠标滚轮时触发;使用光标键和拖动滚动条不会触发该事件。

我阅读了文档和示例:跨浏览器监听此事件
经过在FF、IE、Chrome、Safari上的一些测试,我最终得到了这段代码片段:

//creates an element to print the scroll position
$("<p id='test'>").appendTo("body").css({
    padding: "5px 7px",
    background: "#e9e9e9",
    position: "fixed",
    bottom: "15px",
    left: "15px"
});

//attach the "wheel" event if it is supported, otherwise "mousewheel" event is used
$("html").on(("onwheel" in document.createElement("div") ? "wheel" : "mousewheel"), function (e) {
    var evt = e.originalEvent || e;

    //this is what really matters
    var deltaY = evt.deltaY || (-1 / 40 * evt.wheelDelta), //wheel || mousewheel
        scrollTop = $(this).scrollTop() || $("body").scrollTop(), //fix safari
        scrollText = "";

    if (deltaY > 0) {
        scrollText = "<b>scroll down</b>";
    } else {
        scrollText = "<b>scroll up</b>";
    }

    //console.log("Event: ", evt);
    $("#test").html(scrollText +
      "<br>clientHeight: " + this.clientHeight +
      "<br>scrollHeight: " + this.scrollHeight +
      "<br>scrollTop: " + scrollTop +
      "<br>deltaY: " + deltaY);
});

2
我有一个固定的面板视图,它禁用了实际的滚动条,因此我需要检测鼠标滚轮的方向。您的鼠标滚轮解决方案在跨浏览器方面完美地运行,所以非常感谢! - Shannon Hochkins

4

更新于2021年04月07日

Bobort在评论中指出了滚轮文档中新增的注意事项:

不要将滚轮事件与滚动事件混淆。滚轮事件的默认操作是特定于实现的,并不一定会触发滚动事件。即使它触发了,滚轮事件中的delta*值并不一定反映内容的滚动方向。因此,请勿依赖滚轮事件的delta*属性来获取滚动方向。相反,检测滚动事件中目标的scrollLeft和scrollTop的值变化。

然而,文档中给出的示例使用delta进行缩放,这意味着向上滚动为缩小,向下滚动为放大。

以下代码在不同的浏览器中对我有效,用于检测方向,但鉴于该注意事项,请自行决定是否使用。

原始答案

自从 v3 版本开始,bind已被弃用(取而代之的是on),并且wheel现在得到支持, 不再使用wheelDelta

$(window).on('wheel', function(e) {
  if (e.originalEvent.deltaY > 0) {
    console.log('down');
  } else if (e.originalEvent.deltaY < 0) {
    console.log('up');
  } else if (e.originalEvent.deltaX > 0) {
    console.log('right');
  } else if (e.originalEvent.deltaX < 0) {
    console.log('left');
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 style="white-space:nowrap;overflow:scroll">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
</h1>

wheel事件的MDN上的浏览器兼容性(2019-03-18)

Compatibility of the wheel event


上面的代码会产生两个控制台日志,使用以下代码可以完全分离出向上/向下/向左/向右: console.log('向下'); } else if(e.originalEvent.deltaY < 0) { console.log('向上'); } else if(e.originalEvent.deltaX > 0) { console.log('向右'); } else if(e.originalEvent.deltaX < 0) { console.log('向左'); } - Don Wilson
对于移动设备,请使用 touchmove 事件。 - CPHPython
我查看了文档,当检测滚动方向时,它明确指出:“不要依赖于滚轮事件的delta*属性来获取滚动方向。相反,在滚动事件中检测目标的scrollLeftscrollTop值的变化。” - Bobort
1
感谢 @Bobort,我根据您的注释进行了调整。 - CPHPython
@DonWilson 谢谢您!刚刚更新了答案,加入了您的建议。 - CPHPython

3
为了忽略页面顶部和底部的任何快照/动量/反弹效果,这里是经过修改的Josiah的被接受答案的版本:链接
var prevScrollTop = 0;
$(window).scroll(function(event){

    var scrollTop = $(this).scrollTop();

    if ( scrollTop < 0 ) {
        scrollTop = 0;
    }
    if ( scrollTop > $('body').height() - $(window).height() ) {
        scrollTop = $('body').height() - $(window).height();
    }

    if (scrollTop >= prevScrollTop && scrollTop) {
        // scrolling down
    } else {
        // scrolling up
    }

    prevScrollTop = scrollTop;
});

3
这段代码在IE、Firefox、Opera和Chrome中都能正常运行:
$(window).bind('wheel mousewheel', function(event) {
      if (event.originalEvent.deltaY >= 0) {
          console.log('Scroll down');
      }
      else {
          console.log('Scroll up');
      }
  });

'wheel mousewheel'和属性deltaY必须在bind()函数中使用。

记住:为了安全起见,用户必须更新其系统和浏览器。在2018年,“我有IE 7”的借口是无稽之谈。我们必须教育用户。

祝您拥有愉悦的一天 :)


1
我尝试了你的代码,但是效果相反。第一个触发器在向下滚动时触发,第二个触发器在向上滚动时触发。 - AlexioVay
嗨 :) 感谢AlexioVay。我编辑了我的代码(终于到时候了!)^^。当然,“console.log('.......')”只是我们可以做的示例。 - Cyril Morales

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接