Есть ли способ обнаружить горизонтальную прокрутку только без запуска переполнения браузера
Вы можете обнаружить событие прокрутки браузера на произвольном элементе с помощью:
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)
}
})
})
Ответ 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
.
Ответ 6
Вы можете использовать API-интерфейс Intersection Observer
Там имеется полисполк w3c, поскольку нативная поддержка недостаточно распространена.
Полипол заполняет события прокрутки
Ответ 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;
}
});
Нужно время, чтобы дважды проверить, какой метод является самым быстрым...