次のようにして、任意の要素のブラウザスクロールイベントを検出できます。
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日の終わりに、スクロール方向を見つけるためにこれらのプロパティの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
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;
}
...そして、DOM全体のリフローによるボトルネックを心配することなく、元の質問と同じようにelement.scrollLeft
およびelement.scrollTop
属性を自由に読むことができます。
このアプローチも Googleの開発者ガイドラインで推奨 です。
fast-domライブラリを試してください:
ローカルで サンプルコード をローカルで実行して、正しいプロファイリングデータを取得します。
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)
}
})
})
入力を直接聞くのはどうですか?
element.addEventListener('wheel', e => {
if ( e.deltaX !== 0 ) {
horizontal();
}
if ( e.deltaY !== 0 ) {
vertical();
}
})
また、独自のスクロールバーを作成し、それらのドラッグを待機する必要がある場合もあります。車輪を再発明していますが(笑)、まあscrollTop
やscrollLeft
は見えません。
@Nelsonの回答で述べたように、水平スクロールと垂直スクロールの間をチェックする方法はありません、そしてその理由は、トリガーされたイベントオブジェクトがゼロに関する情報を持っているためですそれをチェックしました。
Mozillaには、要素のプロパティチェックを抑制する方法の 一連の例 がありますが、私は個人的にsetTimeout
アプローチを好みます。ニーズに合わせてFPS応答を微調整できるからです。そのことを念頭に置いて、私は次のRead-to-Useの例を書きました。
<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>
Intersection Observer API を使用できます
ネイティブサポート が十分に普及していないため、 w3c polyfill を使用できます。
ポリフィルはスクロールイベントをデバウンスします
私は投資してきましたが、このソリューションを思いつきました:
水平方向にスクロールするとき、cssプロパティを2つの要素に変更する必要がありました。それがこの側面を制御したい人に役立つことを願っています。乾杯!
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;
}
})
})