Javascript 有没有办法只检测水平滚动而不触发浏览器回流

Javascript 有没有办法只检测水平滚动而不触发浏览器回流,javascript,html,reflow,Javascript,Html,Reflow,您可以通过以下方式检测任意元素上的浏览器滚动事件: element.addEventListener('scroll', function (event) { // do something }); 我希望能够区分垂直滚动和水平滚动,并为它们独立执行操作 我目前正在通过将element.scrollTop和element.scrollLeft的值隐藏起来,然后在事件侦听器中比较它们来实现这一点。例如 var scrollLeft, scrollTop; element.addEvent

您可以通过以下方式检测任意元素上的浏览器滚动事件:

element.addEventListener('scroll', function (event) {
    // do something
});
我希望能够区分垂直滚动和水平滚动,并为它们独立执行操作

我目前正在通过将element.scrollTop和element.scrollLeft的值隐藏起来,然后在事件侦听器中比较它们来实现这一点。例如

var scrollLeft, scrollTop;
element.addEventListener('scroll', function (event) {
    if (scrollLeft !== element.scrollLeft) {
        // horizontally scrolled

        scrollLeft = element.scrollLeft;
    }

    if (scrollTop !== element.scrollTop) {
        // vertically scrolled

        scrollTop = element.scrollTop;
    }
});
这很好,但是从我读到的scrollLeft或scrollTop值会导致回流


有没有更好的方法可以做到这一点而不会导致浏览器在滚动时出现回流?

我想实际上没有

但根据你的想法,你可以尝试:

var lastScrollLeft = 0;
$(window).scroll(function() {
    var documentScrollLeft = $(document).scrollLeft();
    if (lastScrollLeft != documentScrollLeft) {
        console.log('scroll x');
        lastScrollLeft = documentScrollLeft;
    }
});

需要计时以再次检查哪种方法是最快的…

我认为您的代码是正确的,因为在一天结束时,您需要阅读其中一个属性以找出滚动方向,但避免性能问题的关键是限制事件的发生,因为否则,
滚动
事件触发太频繁,这是性能问题的根本原因

因此,适用于限制
滚动
事件的示例代码如下:

var ticking = false;
var lastScrollLeft = 0;
$(window).scroll(function() {
    if (!ticking) {
        window.requestAnimationFrame(function() {

            var documentScrollLeft = $(document).scrollLeft();
            if (lastScrollLeft != documentScrollLeft) {
                console.log('scroll x');
                lastScrollLeft = documentScrollLeft;
            }

            ticking = false;
        });
        ticking = true;
    }
});
来源:

尝试库:

在本地运行以获取正确的分析数据

import fastdom from 'fastdom'

let scrollLeft
let scrollTop
const fast = document.getElementById(`fast-dom`)

fast.addEventListener(`scroll`, function fastDom() {
  fastdom.measure(() => {
    if (scrollLeft !== fast.scrollLeft) {
      scrollLeft = fast.scrollLeft
      console.log(scrollLeft)
    }
    if (scrollTop !== fast.scrollTop) {
      scrollTop = fast.scrollTop
      console.log(scrollTop)
    }
  })
})


使用
位置的元素:绝对从文档流中取出

考虑到这一点,您可以使用CSS将
元素绝对定位在其父元素中

#parent{
    width: 500px;
    height: 100px; 
    // ...or whatever dimensions you need the scroll area to be
    position: relative;
}
#element{
    position: absolute;
    top: 0px;
    left: 0px;
    bottom: 0px;
    right: 0px;
}
。。。然后,您可以像在原始问题中一样随意阅读
元素.scrollLeft
元素.scrollTop
属性,而不必担心重新加载整个DOM会出现瓶颈


这种方法也是如此。

正如@Nelson在回答中所说的,无法在水平滚动和垂直滚动之间进行检查,原因是触发的事件对象没有关于它的任何信息(我刚刚检查了它)

Mozilla知道如何限制元素属性检查,但我个人更喜欢
setTimeout
方法,因为您可以根据需要微调FPS响应。考虑到这一点,我写了以下阅读示例:

<html>
<head>
  <meta charset="utf-8"/>
  <style>
  #foo {
    display: inline-block;
    width: 500px;
    height: 500px;
    overflow: auto;
  }
  #baz {
    display: inline-block;
    background: yellow;
    width: 1000px;
    height: 1000px;
  }
  </style>
</head>
<body>
  <div id="foo">
    <div id="baz"></div>
  </div>
  <script>
    // Using ES5 syntax, with ES6 classes would be easier.
    function WaitedScroll(elem, framesPerSecond, onHorz, onVert) {
      this.isWaiting = false;
      this.prevLeft = 0;
      this.prevTop = 0;
      var _this = this;

      elem.addEventListener('scroll', function() {
        if (!_this.isWaiting) {
          _this.isWaiting = true;
          setTimeout(function() {
            _this.isWaiting = false;
            var curLeft = elem.scrollLeft;
            if (_this.prevLeft !== curLeft) {
              _this.prevLeft = curLeft;
              if (onHorz) onHorz();
            }
            var curTop = elem.scrollTop;
            if (_this.prevTop !== curTop) {
              _this.prevTop = curTop;
              if (onVert) onVert();
            }
          }, 1000 / framesPerSecond);
        }
      });
    }

    // Usage with your callbacks:
    var waiter = new WaitedScroll(
      document.getElementById('foo'), // target object to watch
      15, // frames per second response
      function() { // horizontal scroll callback
        console.log('horz');
      },
      function() { // vertical scroll callback
        console.log('veeeeeeeertical');
      }
    );
  </script>
</body>
</html>

#福{
显示:内联块;
宽度:500px;
高度:500px;
溢出:自动;
}
#巴兹{
显示:内联块;
背景:黄色;
宽度:1000px;
高度:1000px;
}
//使用ES5语法,使用ES6类会更容易。
函数WaitedScroll(元素、帧秒、onHorz、onVert){
this.isWaiting=false;
这个。左=0;
这个.prevTop=0;
var_this=这个;
元素addEventListener('scroll',函数(){
如果(!\u这个正在等待){
_this.isWaiting=true;
setTimeout(函数(){
_this.isWaiting=false;
var curLeft=elem.scrollLeft;
if(_this.prevleet!==curLeft){
_this.prevlet=curLeft;
if(onHorz)onHorz();
}
var curTop=元素滚动顶部;
如果(_this.prevTop!==curTop){
_this.prevTop=curTop;
if(onVert)onVert();
}
},1000/帧每秒);
}
});
}
//回调的用法:
var waiter=新的waitedcoll(
document.getElementById('foo'),//要监视的目标对象
15,//每秒响应帧数
函数(){//水平滚动回调
console.log('horz');
},
函数(){//垂直滚动回调
console.log('veeeeeerical');
}
);

直接听输入怎么样

element.addEventListener('wheel', e => {
    if ( e.deltaX !== 0 ) {
        horizontal();
    }
    if ( e.deltaY !== 0 ) {
        vertical();
    }
})
您可能还需要创建自己的滚动条,并监听滚动条上的拖动。它正在重新发明轮子(lol),但嘿,看不到
scrollTop
scrollLeft

你可以使用

有一种可用的方法,因为它不够广泛


polyfill debounces scroll活动

我一直在投资,我提出了以下解决方案:

在水平滚动时,我必须将css属性更改为两个元素。希望它能帮助任何想要控制这方面的人。干杯

var lastScrollLeft= 0;
$(window).scroll(function(){
    var position= $(document).scrollLeft();
    window.requestAnimationFrame(function() {
    if(position == 0){
            $("#top_box, #helper").css("position","fixed");
    }else if(position !== lastScrollLeft){
            $("#top_box, #helper").css("position","absolute");
            lastScrollLeft= position;
    }
    })
    })

跟踪元素的
scrollLeft
scrollTop
值,查看它们在处理滚动事件时是否发生了更改,并在处理结束时进行更新,以便为下一次事件处理做好准备?如果没有在事件之间更改DOM结构,则不会触发回流,或者更准确地说,回流与此无关。Nelson是对的,你无论如何都应该限制滚动事件,但这不是因为回流,只是因为对这个“图形”事件的反应速度快于屏幕刷新速度没有什么意义。是的,使用rAF限制这个事件是很好的,如果您不想
preventDefault
事件,使用被动事件可能更好。回答得很好,谢谢。。。当使用getBoundingClientRect查找上/左属性(如视差滚动)时,这个简单的技巧可以大大提高性能。这应该是鼠标滚轮的公认答案。水平拖动滚动条无法到达此代码,因为发出的DOM事件不同。