簡體   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