检测滚动方向

220

我正在尝试使用JavaScript的on scroll调用函数。但是我想知道是否可以在不使用jQuery的情况下检测滚动的方向。如果不行,那么有没有什么解决方法?

我考虑只放置一个“返回顶部”按钮,但如果可能的话,我想避免这样做。

我已经尝试了以下代码,但它没有起作用:

if document.body.scrollTop <= 0 {
    alert ("scrolling down")
} else {
    alert ("scrolling up")
}

这是事件的 wheelDelta。在不同浏览器中可能会有差异。请参考 http://phrogz.net/js/wheeldelta.html。 - marekful
1
嗯,不太是我想要的,但感谢你的帮助 :P 有其他建议吗? - dwinnbrown
18个回答

320

通过存储先前的 scrollTop 值并将当前的 scrollTop 值与之进行比较,可以检测到它。

JavaScript:

var lastScrollTop = 0;

// element should be replaced with the actual target element on which you have applied scroll, use window in case of no target element.
element.addEventListener("scroll", function(){ // or window.addEventListener("scroll"....
   var st = window.pageYOffset || document.documentElement.scrollTop; // Credits: "https://github.com/qeremy/so/blob/master/so.dom.js#L426"
   if (st > lastScrollTop) {
      // downscroll code
   } else if (st < lastScrollTop) {
      // upscroll code
   } // else was horizontal scroll
   lastScrollTop = st <= 0 ? 0 : st; // For Mobile or negative scrolling
}, false);

52
最好将 lastScrollTop 的初始化设置为 pageYOffset || scrollTop,而不是假定为 0,这样会更安全。 - Ed Ballot
1
@Prateek 谢谢你的回答,但对我没有用... 我正在尝试在使用Tumult Hype构建的Web应用程序中“更改场景”。 - dwinnbrown
1
什么是负向滚动?根据这个答案,浏览器不允许负向滚动,那么它有什么作用呢? - mockingjay
1
如果当前位置在列表顶部,先前的scrollTop和当前位置都等于0,那该怎么办呢? - Hoangdz
1
触摸设备允许略微滚动超出边界。这为用户提供了视觉提示,表明他们已经到达了滚动的极限。当您在此类动画期间查询DOM属性时,可能会发现超出边界的值。<= 0的测试处理了这种情况。另一种类似的情况可能会在另一端出现。 - Jake
显示剩余9条评论

91

捕获所有滚动事件(触摸和鼠标滚轮)的简单方法

window.onscroll = function(e) {
  // print "false" if direction is down and "true" if up
  console.log(this.oldScroll > this.scrollY);
  this.oldScroll = this.scrollY;
}

23
欢迎来到SO,如果您在回答中添加描述,这将对OP和其他人更有帮助。 - Alejandro Montilla

62

使用此功能查找滚动方向。仅用于查找垂直滚动的方向。支持所有浏览器。

var scrollableElement = document.body; //document.getElementById('scrollableElement');

scrollableElement.addEventListener('wheel', checkScrollDirection);

function checkScrollDirection(event) {
  if (checkScrollDirectionIsUp(event)) {
    console.log('UP');
  } else {
    console.log('Down');
  }
}

function checkScrollDirectionIsUp(event) {
  if (event.wheelDelta) {
    return event.wheelDelta > 0;
  }
  return event.deltaY < 0;
}

示例


3
这很好,但似乎只适用于使用滚轮。 - Jonathan.Brink
14
注意:不要将轮滚事件与滚动事件混淆。轮滚事件的默认行为是与实现相关的,并且不一定会触发一个滚动事件。即使它触发了滚动事件,轮滚事件的delta值也不一定反映内容的滚动方向。因此,请不要依赖于轮滚事件的delta属性来获取滚动方向。相反,在滚动事件中检测目标的scrollLeft和scrollTop值的变化。https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event - prettyInPink
1
这个事件(相比于“滚动”事件)更好,因为它在到达滚动位置的末尾或开头后也会触发 - 有时您可能希望触发某些特定操作“滚动到”元素的末尾。 - lenooh
2
谢谢!在“fixed”定位的部分非常有用! - Oleksa O.

20

你可以尝试这样做。

function scrollDetect(){
  var lastScroll = 0;

  window.onscroll = function() {
      let currentScroll = document.documentElement.scrollTop || document.body.scrollTop; // Get Current Scroll Value

      if (currentScroll > 0 && lastScroll <= currentScroll){
        lastScroll = currentScroll;
        document.getElementById("scrollLoc").innerHTML = "Scrolling DOWN";
      }else{
        lastScroll = currentScroll;
        document.getElementById("scrollLoc").innerHTML = "Scrolling UP";
      }
  };
}


scrollDetect();
html,body{
  height:100%;
  width:100%;
  margin:0;
  padding:0;
}

.cont{
  height:100%;
  width:100%;
}

.item{
  margin:0;
  padding:0;
  height:100%;
  width:100%;
  background: #ffad33;
}

.red{
  background: red;
}

p{
  position:fixed;
  font-size:25px;
  top:5%;
  left:5%;
}
<div class="cont">
  <div class="item"></div>
  <div class="item red"></div>
  <p id="scrollLoc">0</p>
</div>


这对我来说不起作用。当我向上滚动到一定高度时,它会显示在下面。 - Developer

12
  1. 初始化oldValue
  2. 监听事件获取newValue
  3. 将两者相减
  4. 根据结果得出结论
  5. 使用newValue更新oldValue

// 初始化

let oldValue = 0;

//监听事件

window.addEventListener('scroll', function(e){

    // Get the new Value
    newValue = window.pageYOffset;

    //Subtract the two and conclude
    if(oldValue - newValue < 0){
        console.log("Up");
    } else if(oldValue - newValue > 0){
        console.log("Down");
    }

    // Update the old value
    oldValue = newValue;
});

10

尽管已接受的答案有效,但值得注意的是,这将以高速率触发。这可能会对计算密集型操作造成性能问题。

MDN的建议是限制事件频率。以下是他们示例的修改版本,增强了检测滚动方向的功能。

修改自:https://developer.mozilla.org/en-US/docs/Web/API/Document/scroll_event

// ## function declaration
function scrollEventThrottle(fn) {
  let last_known_scroll_position = 0;
  let ticking = false;
  window.addEventListener("scroll", function () {
    let previous_known_scroll_position = last_known_scroll_position;
    last_known_scroll_position = window.scrollY;
    if (!ticking) {
      window.requestAnimationFrame(function () {
        fn(last_known_scroll_position, previous_known_scroll_position);
        ticking = false;
      });
      ticking = true;
    }
  });
}

// ## function invocation
scrollEventThrottle((scrollPos, previousScrollPos) => {
    if (previousScrollPos > scrollPos) {
      console.log("going up");
    } else {
      console.log("going down");
    }
});


9

这是对prateek回答的补充。在IE浏览器中,代码似乎存在一些问题,所以我决定稍微修改一下它,没有什么花哨的东西(只是添加了另一个条件)。

$('document').ready(function() {
var lastScrollTop = 0;
$(window).scroll(function(event){
   var st = $(this).scrollTop();
   if (st > lastScrollTop){
       console.log("down")
   }
   else if(st == lastScrollTop)
   {
     //do nothing 
     //In IE this is an important condition because there seems to be some instances where the last scrollTop is equal to the new one
   }
   else {
      console.log("up")
   }
   lastScrollTop = st;
});});

1
谢谢您的提示...这似乎与IE的“平滑滚动”选项有关。 - Kristo

5

下面这段简单的代码是可行的:在控制台中查看结果。

let scroll_position = 0;
let scroll_direction;

window.addEventListener('scroll', function(e){
    scroll_direction = (document.body.getBoundingClientRect()).top > scroll_position ? 'up' : 'down';
    scroll_position = (document.body.getBoundingClientRect()).top;
    console.log(scroll_direction);
});

调用 getBoundingClientRect 会在每次调用时强制对元素(在本例中为整个 body)进行布局。因此,最好在每次滚动事件中仅调用一次并缓存结果... - Heretic Monkey

3

如果有人想用 React hooks 来实现它

  const [scrollStatus, setScrollStatus] = useState({
    scrollDirection: null,
    scrollPos: 0,
  });

  useEffect(() => {
    window.addEventListener("scroll", handleScrollDocument);

    return () => window.removeEventListener("scroll", handleScrollDocument);
  }, []);

  function handleScrollDocument() {
    setScrollStatus((prev) => { // to get 'previous' value of state
      return {
        scrollDirection:
          document.body.getBoundingClientRect().top > prev.scrollPos
            ? "up"
            : "down",
        scrollPos: document.body.getBoundingClientRect().top,
      };
    });
  }

  console.log(scrollStatus.scrollDirection)

3
您可以使用document.documentElement.scrollTop获取滚动条位置。然后,只需要将其与先前的位置进行比较即可。

好的,我仍然可以在传统上不允许滚动的网站上使用它吗?(即它适合浏览器的100%宽度和高度)。谢谢。 - dwinnbrown

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