繁体   English   中英

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

[英]Looking for a better (more efficient) way to test for scroll position

这是一个 Javascript 项目,使用 jQuery 库。

当浏览器窗口低于某个页面位置时,我想显示一个粘性栏,否则将其隐藏。 它的 ID 是#sticky-atc-bar

页面位置由类.product-section的元素的顶部确定。

基于对如何做到这一点的研究,我最初想出了以下 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();
            }
        });
    }); 
});

这行得通。 然而,我知道它会在用户与页面互动的整个过程中产生很多滚动事件。 知道这是非常低效的,我去寻找替代方法。 在此过程中,我阅读了这篇名为“从 Twitter 学习”的文章,这完全有道理。

我也遇到了类似要求的解决方案 我的代码版本是:

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);
    }
});

我在这个 jsFiddle上设置了它,它可以工作。

但是......它仍然会产生大量事件。 它们被简单地减少为每 250 毫秒一个事件,因此每秒 4 个。

我想知道是否有更好的方法来解决这个问题? 换句话说,除了更改第二段代码的超时之外,我可以将此操作减少到更少的事件吗? 在这一点上这甚至是必要的,还是每 250 毫秒发生一个事件不会产生重大影响?

给你: 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. 使用InterSectionObserver观察元素相对于视口的位置。 使用该元素在视口中的位置来“做其他事情”,即显示/隐藏另一个元素
  2. 避免使用setTimeout来操作 DOM - 改用requestAnimationFrame (此处不需要)
  3. 避免重复查询 DOM 元素 - 获取一次,然后在需要操作的地方引用它们
  4. 更喜欢在 CSS 中设置默认值,然后在 JS 中覆盖内联的特定属性,当您不再需要该属性时将其删除

推理:

IntersectionObserver允许您观察任意元素相对于其在视口中的位置,而不是在滚动时快速触发的滚动事件。 观察到的元素触发的事件仅在其相对于视口的位置发生变化时触发

setTimeout - 如果您要使用超时,您需要考虑一些适合在使用setTimeout时使用的数字。 你怎么做到这一点? 好吧,你只能猜测......你必须使用一个幻数。 requestAnimationFrame而是知道 DOM 何时准备好重新渲染和计算布局 - 不需要幻数


链接:


编辑:随意用$(selector)替换document.querySelector - 其余代码将按原样工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM