简体   繁体   English

寻找更好(更有效)的方法来测试滚动位置

[英]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
  }
}
  1. use 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 element使用该元素在视口中的位置来“做其他事情”,即显示/隐藏另一个元素
  2. avoid setTimeout for manipulating the DOM - use requestAnimationFrame instead (not required here)避免使用setTimeout来操作 DOM - 改用requestAnimationFrame (此处不需要)
  3. avoid querying DOM elements repeatedly - get them once, and reference them where they need to be manipulated避免重复查询 DOM 元素 - 获取一次,然后在需要操作的地方引用它们
  4. prefer setting defaults in CSS, and then in JS override a specific property inline, removing the property when you no longer need it更喜欢在 CSS 中设置默认值,然后在 JS 中覆盖内联的特定属性,当您不再需要该属性时将其删除

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.

相关问题 有没有更好/更有效的方法来实现这一目标? - Is there a better/more efficient way to achieve this? 寻找一种更有效的方式编写此jquery菜单 - Looking for a more efficient way to write this jquery menu 寻找一种更有效的方式来编写这个 Javascript - Looking for a more efficient way to write this Javascript 是否有更好(更有效)的方式来运行href? - Is there a better (more efficient) way to run an href? 寻找更有效的方法来设置和更改顺序的骨干模型属性 - Looking for a more efficient way to set and alter sequential Backbone model attributes 寻找一种更有效的方法来隐藏此溢出并转换文本上的动画 - Looking for a more efficient way of creating this overflow hidden and translate animation on text 什么是更好的和更有效的方法来执行此功能的阻塞? - What is a better and more efficient way to do this blocking of a function? 有没有更有效或更一般的方法来确定数字是否为整数? - Is there a more efficient or generally better way to determine if a number is an integer than this? 使用jQuery基于浏览器滚动位置添加CSS类 - 寻找更模块化的方法 - Adding a CSS class based on browser scroll position with jQuery - looking for a more modular approach 有没有更有效的方式来编写此代码? - Is there a more efficient way to write this?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM