Есть ли способ обнаружить горизонтальную прокрутку только без запуска переполнения браузера

Вы можете обнаружить событие прокрутки браузера на произвольном элементе с помощью:

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;
    }
});

Это работает отлично, однако из https://gist.github.com/paulirish/5d52fb081b3570c81e3a я читал, что чтение значений scrollLeft или scrollTop вызывает оплату.

Есть ли лучший способ сделать это, не заставляя браузер переходить на свиток?

Ответы

Ответ 1

Я думаю, что ваш код прав, потому что в конце дня вам нужно прочитать одно из этих свойств, чтобы узнать направление прокрутки, но ключевая вещь здесь, чтобы избежать проблем с производительностью, - это дросселировать событие, потому что в противном случае срабатывает событие scroll слишком часто, и это является основной причиной проблем с производительностью.

Таким образом, ваш примерный код, предназначенный для дросселирования события scroll будет выглядеть так:

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;
    }
});

Источник: https://developer.mozilla.org/en-US/docs/Web/Events/scroll#Example

Ответ 2

Элементы, которые используют position: absolute; выводятся из потока документов (см. источник).

Имея это в виду, вы можете использовать CSS, чтобы сделать ваш element абсолютно позиционированным внутри своего родителя...

#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;
}

... и тогда вы можете свободно читать element.scrollLeft и element.scrollTop как в своем исходном вопросе, не опасаясь, чтобы узкие места переполняли всю DOM.

Этот подход также рекомендуется в рекомендациях разработчика Google.

Ответ 3

Попробуйте библиотеку быстрого доступа:

Запустите код примера на вашей стороне, чтобы получить правильные данные профилирования.

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)
    }
  })
})

enter image description here

Edit fast-dom eaxmple

Ответ 4

Как сказано в @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>

Ответ 5

Как насчет прослушивания ввода напрямую?

element.addEventListener('wheel', e => {
    if ( e.deltaX !== 0 ) {
        horizontal();
    }
    if ( e.deltaY !== 0 ) {
        vertical();
    }
})

Вам также может потребоваться создать свои собственные полосы прокрутки и прослушать их перетаскивание. Он изобретает колесо (lol), но не scrollTop scrollLeft в нем не было scrollTop или scrollLeft.

Ответ 7

Я инвестировал, и я придумаю это решение:

Мне пришлось изменить свойство 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;
    }
    })
    })

Ответ 8

Я ожидаю, что на самом деле нет...

Но согласно Как определить горизонтальную прокрутку в jQuery? вы можете попробовать:

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

Нужно время, чтобы дважды проверить, какой метод является самым быстрым...