Vous pouvez détecter un événement de défilement du navigateur sur un élément arbitraire avec:
element.addEventListener('scroll', function (event) {
// do something
});
J'aimerais pouvoir faire la distinction entre le défilement vertical et le défilement horizontal et exécuter des actions pour eux indépendamment.
Je le fais actuellement en stockant les valeurs de element.scrollTop et element.scrollLeft, puis en les comparant dans l'écouteur d'événements. Par exemple.
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;
}
});
Cela fonctionne bien, cependant, à partir de https://Gist.github.com/paulirish/5d52fb081b3570c81e3a J'ai lu que la lecture des valeurs scrollLeft ou scrollTop provoque un refoulement.
Y a-t-il une meilleure façon de faire cela sans provoquer un reflow du navigateur sur le défilement?
Je pense que votre code est correct, car à la fin de la journée, vous devez lire l’une de ces propriétés pour connaître la direction de défilement, mais le élément clé pour éviter les problèmes de performances est de limiter le event, sinon l'événement scroll
se déclenche trop souvent et constitue la cause première des problèmes de performances.
Ainsi, votre exemple de code adapté pour réguler l'événement scroll
serait le suivant:
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;
}
});
Source: https://developer.mozilla.org/en-US/docs/Web/Events/scroll#Example
Les éléments qui utilisent position: absolute;
sont extraits du flux de documents (voir source) .
En gardant cela à l'esprit, vous pouvez utiliser CSS pour que votre element
soit positionné de manière absolue dans son parent ...
#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;
}
... et ensuite vous pouvez lire les attributs element.scrollLeft
et element.scrollTop
comme vous le faisiez dans votre question initiale sans crainte de goulets d'étranglement dus à la refusion de l'ensemble du DOM.
Cette approche est également recommandée dans les consignes de développement de Google .
Essayez la bibliothèque fast-dom:
Exécutez l'exemple de code de votre côté localement pour obtenir les données de profilage correctes.
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)
}
})
})
Qu'en est-il d'écouter l'entrée directement?
element.addEventListener('wheel', e => {
if ( e.deltaX !== 0 ) {
horizontal();
}
if ( e.deltaY !== 0 ) {
vertical();
}
})
Vous devrez peut-être aussi créer vos propres barres de défilement et écouter pour les déplacer. C'est réinventer la roue (lol) mais bon, il n'y a pas scrollTop
ou scrollLeft
en vue.
Comme indiqué dans la réponse de @ Nelson, il n'y a aucun moyen de vérifier entre le défilement horizontal et le défilement vertical. La raison en est que l'objet événement déclenché ne contient aucune information (je viens de le vérifier).
Mozilla a de nombreux exemples sur la manière de limiter la vérification des propriétés de l'élément, mais je préfère personnellement l'approche setTimeout
, car vous pouvez ajuster la réponse FPS en fonction de vos besoins. C'est dans cet esprit que j'ai écrit cet exemple de lecture-utilisation:
<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>
Vous pouvez utiliser API Intersection Observer
Il existe un w3c polyfill disponible depuis le support natif n'est pas assez répandu.
Le polyfill annule les événements de défilement