[英]Looking for a better (more efficient) way to test for scroll position
This is a Javascript project, using the jQuery library.这是一个 Javascript 项目,使用 jQuery 库。
I have a sticky bar I would like to show whenever the browser window is below a certain page position, and otherwise hide it.当浏览器窗口低于某个页面位置时,我想显示一个粘性栏,否则将其隐藏。 Its ID is
#sticky-atc-bar
.它的 ID 是
#sticky-atc-bar
。
The page position is determined by the top of an element with class .product-section
.页面位置由类
.product-section
的元素的顶部确定。
Based on research into how to do this, I originally came up with the following JS.基于对如何做到这一点的研究,我最初想出了以下 JS。
$( document ).ready(function() {
$('#sticky-atc-bar').hide();
var section = $(".product-section");
var offsetSection = section.offset().top; //check for top property
$(function() {
$(window).scroll(function() {
console.log('Scroll Event');
if ($(window).scrollTop() >= offsetSection) { // reached product section
console.log('True:');
$('#sticky-atc-bar').show();
} else { // not reached product section
console.log('False');
$('#sticky-atc-bar').hide();
}
});
});
});
This works.这行得通。 Yet, I am aware it generates a lot of scroll events, the entire time the user is engaged with the page.
然而,我知道它会在用户与页面互动的整个过程中产生很多滚动事件。 Knowing that's very inefficient, I went looking for alternative approaches.
知道这是非常低效的,我去寻找替代方法。 Along the way I read this article called "Learning from Twitter", which made total sense.
在此过程中,我阅读了这篇名为“从 Twitter 学习”的文章,这完全有道理。
I also came across this solution to a similar requirement.我也遇到了类似要求的解决方案。 My version of that code is:
我的代码版本是:
var target = $(".product-section").offset().top,
timeout = null;
$('#sticky-atc-bar').hide();
$(window).scroll(function () {
if (!timeout) {
timeout = setTimeout(function () {
console.log('scroll');
clearTimeout(timeout);
timeout = null;
if ($(window).scrollTop() >= target) {
$('#sticky-atc-bar').show();
} else {
$('#sticky-atc-bar').hide();
}
}, 250);
}
});
I set it up on this jsFiddle , and it works.我在这个 jsFiddle上设置了它,它可以工作。
However ... It still generates a significant number of events.但是......它仍然会产生大量事件。 They have simply been reduced to one event every 250 ms, so 4 every second.
它们被简单地减少为每 250 毫秒一个事件,因此每秒 4 个。
What I would like to know is if there's a better way to go about this?我想知道是否有更好的方法来解决这个问题? Put another way, aside from changing the timeout on this second piece of code, can I reduce this operation to even fewer events?
换句话说,除了更改第二段代码的超时之外,我可以将此操作减少到更少的事件吗? And is that even necessary at this point, or is one event every 250ms not a significant impact?
在这一点上这甚至是必要的,还是每 250 毫秒发生一个事件不会产生重大影响?
Here you go: https://jsfiddle.net/son0azfj/给你: https ://jsfiddle.net/son0azfj/
var productEl = document.querySelector('.product-section')
var stickyEl = document.querySelector('#sticky-atc-bar')
// check if we have elements
if (productEl && stickyEl) {
// create an observer object, binding toggleDisplayFactory to it
var observer = new IntersectionObserver(toggleDisplayFactory(stickyEl))
// observe the product element
observer.observe(productEl)
}
// [1] create a named function which accepts an element...
function toggleDisplayFactory(element) {
// and returns a function - the handler that IntersectionObserver expects
return function handler(entries) {
// determine if the observed element is in the viewport or not
var isInViewPort = Boolean(entries.find(entry => entry.intersectionRatio > 0))
/**
* if in the viewport:
* we want the element passed in at [1] to use it's own styles, i.e. remove the inline style property
* else:
* we want the element to inherit its default display property (e.g. block, inline, inline-block, etc.) and thus be visible
*/
var display = isInViewPort ? null : 'inherit'
// set the display property of the element passed in at [1]
element.style.display = display
}
}
InterSectionObserver
to watch for the position of an element with respect to the viewport.InterSectionObserver
观察元素相对于视口的位置。 Use that element's position in the viewport to "do something else", ie show / hide another elementsetTimeout
for manipulating the DOM - use requestAnimationFrame
instead (not required here)setTimeout
来操作 DOM - 改用requestAnimationFrame
(此处不需要)Reasoning:推理:
IntersectionObserver
allows you to observe an arbitrary element with respect to its position in the viewport, instead of a scroll event which fires rapidly as one scrolls. IntersectionObserver
允许您观察任意元素相对于其在视口中的位置,而不是在滚动时快速触发的滚动事件。 The events an observed element trigger are only triggered when its position relative to the viewport changes观察到的元素触发的事件仅在其相对于视口的位置发生变化时触发
setTimeout
- If you were to use a timeout, you would need to think of some number which is appropriate to use when using setTimeout
. setTimeout
- 如果您要使用超时,您需要考虑一些适合在使用setTimeout
时使用的数字。 How do you do this?你怎么做到这一点? Well, you can only guess... you'd have to use a magic number.
好吧,你只能猜测......你必须使用一个幻数。
requestAnimationFrame
instead knows when the DOM is ready for re-rendering and calculating layout - no magic numbers required requestAnimationFrame
而是知道 DOM 何时准备好重新渲染和计算布局 - 不需要幻数
Links:链接:
EDIT: Feel free to replace document.querySelector
with $(selector)
- the rest of the code will work as-is.编辑:随意用
$(selector)
替换document.querySelector
- 其余代码将按原样工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.