有没有办法只检测水平滚动而不触发浏览器回流
Is there a way to detect horizontal scroll only without triggering a browser reflow
您可以检测任意元素上的浏览器滚动事件:
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 值会导致回流。
有没有更好的方法来做到这一点而不会导致浏览器在滚动时回流?
我预计实际上没有...
但是根据How to detect horizontal scrolling in jQuery?你可以试试:
var lastScrollLeft = 0;
$(window).scroll(function() {
var documentScrollLeft = $(document).scrollLeft();
if (lastScrollLeft != documentScrollLeft) {
console.log('scroll x');
lastScrollLeft = documentScrollLeft;
}
});
需要time-it仔细检查哪种方法最快...
我认为你的代码是正确的,因为在一天结束时你需要阅读其中一个属性来找出滚动方向,但是这里的关键避免性能问题的方法是 限制事件 ,否则 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
尝试fast-dom图书馆:
运行 the example code 在您的本地获取正确的分析数据。
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)
}
})
})
使用 position: absolute;
的元素从文档流中删除 (see source)。
考虑到这一点,您可以使用 CSS 让您的 element
absolutely-positioned 在其父项中...
#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 造成瓶颈。
如@Nelson 的回答所述,无法检查水平和垂直滚动,原因是触发事件对象的相关信息为零(我刚检查过)。
Mozilla 有 a bunch of examples 如何限制元素 属性 检查,但我个人更喜欢 setTimeout
方法,因为你可以 fine-tune 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>
直接听输入怎么样?
element.addEventListener('wheel', e => {
if ( e.deltaX !== 0 ) {
horizontal();
}
if ( e.deltaY !== 0 ) {
vertical();
}
})
您可能还需要创建自己的滚动条并监听拖动它们的情况。它正在重新发明轮子(笑),但是嘿,看不到 scrollTop
或 scrollLeft
。
您可以使用Intersection Observer API
w3c polyfill available since native support 还不够广泛。
polyfill 去抖滚动事件
我一直在投资,我想出了这个解决方案:
水平滚动时,我不得不将 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;
}
})
})
下面我只给了横向滚动事件
const target = document.querySelector('.target');
function eventWheelHorizontal(e) {
// Horizontal
if (e.deltaX != '-0') {
e.preventDefault();
// working
}
}
target.addEventListener('wheel', eventWheelHorizontal, { passive: false });
您可以通过应用这个简单的方法来分享活动。
const target = document.querySelector('.target');
function eventWheel(e) {
// horizontal
if (e.deltaX != '-0') {
e.preventDefault();
// working
}
// Vertical
if (e.deltaY != '-0') {
e.preventDefault();
// working
}
}
target.addEventListener('wheel', eventWheel, { passive: false });
您可以检测任意元素上的浏览器滚动事件:
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 值会导致回流。
有没有更好的方法来做到这一点而不会导致浏览器在滚动时回流?
我预计实际上没有...
但是根据How to detect horizontal scrolling in jQuery?你可以试试:
var lastScrollLeft = 0;
$(window).scroll(function() {
var documentScrollLeft = $(document).scrollLeft();
if (lastScrollLeft != documentScrollLeft) {
console.log('scroll x');
lastScrollLeft = documentScrollLeft;
}
});
需要time-it仔细检查哪种方法最快...
我认为你的代码是正确的,因为在一天结束时你需要阅读其中一个属性来找出滚动方向,但是这里的关键避免性能问题的方法是 限制事件 ,否则 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
尝试fast-dom图书馆:
运行 the example code 在您的本地获取正确的分析数据。
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)
}
})
})
使用 position: absolute;
的元素从文档流中删除 (see source)。
考虑到这一点,您可以使用 CSS 让您的 element
absolutely-positioned 在其父项中...
#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 造成瓶颈。
如@Nelson 的回答所述,无法检查水平和垂直滚动,原因是触发事件对象的相关信息为零(我刚检查过)。
Mozilla 有 a bunch of examples 如何限制元素 属性 检查,但我个人更喜欢 setTimeout
方法,因为你可以 fine-tune 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>
直接听输入怎么样?
element.addEventListener('wheel', e => {
if ( e.deltaX !== 0 ) {
horizontal();
}
if ( e.deltaY !== 0 ) {
vertical();
}
})
您可能还需要创建自己的滚动条并监听拖动它们的情况。它正在重新发明轮子(笑),但是嘿,看不到 scrollTop
或 scrollLeft
。
您可以使用Intersection Observer API
w3c polyfill available since native support 还不够广泛。
polyfill 去抖滚动事件
我一直在投资,我想出了这个解决方案:
水平滚动时,我不得不将 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;
}
})
})
下面我只给了横向滚动事件
const target = document.querySelector('.target');
function eventWheelHorizontal(e) {
// Horizontal
if (e.deltaX != '-0') {
e.preventDefault();
// working
}
}
target.addEventListener('wheel', eventWheelHorizontal, { passive: false });
您可以通过应用这个简单的方法来分享活动。
const target = document.querySelector('.target');
function eventWheel(e) {
// horizontal
if (e.deltaX != '-0') {
e.preventDefault();
// working
}
// Vertical
if (e.deltaY != '-0') {
e.preventDefault();
// working
}
}
target.addEventListener('wheel', eventWheel, { passive: false });