【问题标题】:How can I detect window scroll ended in javascript?如何检测以 javascript 结束的窗口滚动?
【发布时间】:2020-01-31 08:31:41
【问题描述】:

我想在滚动结束后向所选元素添加一个类。如何检测以 JS 结束的滚动?

HTML

<ul class="list" id="wrapper">
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
    <li>6</li>
    <li id="element">7</li>
    <li>8</li>
    <li>9</li>
    <li>10</li>
 </ul>

JS

const element = document.getElementById('element');
const y = element.getBoundingClientRect().top + window.scrollY;


window.scroll({
  top: y,
  behavior: 'smooth'
});

JSBIN EXAMPLE

【问题讨论】:

标签: javascript html scroll


【解决方案1】:

您可以使用requestAnimationFrame 来检测scrollTop 何时大于y

requestAnimationFrameway better,而不是在滚动时设置间隔和事件侦听器,以提高性能。

const element = document.getElementById('element');
const y = element.getBoundingClientRect().top + window.scrollY;


function checkScrollEnd() {
  if ((window.scrollY || document.body.scrollTop || document.documentElement.scrollTop) < y) {
    window.requestAnimationFrame(checkScrollEnd);
  }
  else {
    alert('end of scroll')   
  }
}

window.requestAnimationFrame(checkScrollEnd);


window.scroll({
  top: y,
  behavior: 'smooth'  
});

Example fiddle

【讨论】:

  • 如果滚动被其他滚动丢弃,或者它没有产生任何东西(例如 scrollTo(currentPosition)),这将启动无限 rAF 循环
  • @Kaiido 它只是按照 OP 的要求做,它没有考虑任何上下文
  • 它不会“检测滚动何时结束”不,它会“检测滚动顶部何时大于目标位置”这对滚动动作没有任何说明,并且在我暴露的情况下会失败在我以前的 cmets 中,OP 从未说过可以忽略。
【解决方案2】:

当平滑滚动结束时,本机没有事件可以告诉。

对于这种平滑滚动应该如何发生也没有标准行为,没有定义的持续时间,没有定义的计时函数(Chrome 使用缓入缓出函数,而 Firefox 使用线性函数),此外,还有一个通过对滚动算法的其他调用,smooth-scroll 可以在中间 canceled ,甚至根本没有效果......

所以这种检测并不容易。

我现在找到的最好的方法是启动一个requestAnimationFrame 动力循环,如果我们的目标位于同一位置,它将检查每个绘画帧(right after 滚动操作)。一旦它在同一位置超过两帧,我们就假设滚动结束。那时我们可以检查它是否成功,只需检查我们是否在预期的位置:

const trigger = document.getElementById( 'trigger' );
const scroll_forcer = document.getElementById( 'scroll_forcer' );

let scrolling = false; // a simple flag letting us know if we're already scrolling
trigger.onclick = (evt) => startScroll();

function startScroll() {
  setTimeout(()=> {
    scroll_forcer.classList.add( "scrolling" )
    smoothScrollTo( { top: 1000 } )
      .catch( (err) => {
        /*
          here you can handle when the smooth-scroll
          gets disabled by an other scrolling
        */
        console.error( 'failed to scroll to target' );
      } )
      // all done, lower the flag
      .then( () => scroll_forcer.classList.remove( "scrolling" ) );
    }, 10);
};


/* 
 *
 * Promised based window.scrollTo( { behavior: 'smooth' } )
 * @param { Element } elem
 **  ::An Element on which we'll call scrollIntoView
 * @param { object } [options]
 **  ::An optional scrollToOptions dictionary
 * @return { Promise } (void)
 **  ::Resolves when the scrolling ends
 *
 */
function smoothScrollTo( options ) {
  return new Promise( (resolve, reject) => {
    const elem = document.scrollingElement;
    let same = 0; // a counter

    // last known scroll positions
    let lastPos_top = elem.scrollTop;
    let lastPos_left = elem.scrollLeft;

    // pass the user defined options along with our default
    const scrollOptions = Object.assign( {
        behavior: 'smooth',
        top: lastPos_top,
        left: lastPos_left
      }, options );

    // expected final position
    const maxScroll_top = elem.scrollHeight - elem.clientHeight;
    const maxScroll_left = elem.scrollWidth - elem.clientWidth;
    const targetPos_top = Math.max( 0, Math.min(  maxScroll_top, scrollOptions.top ) );
    const targetPos_left = Math.max( 0, Math.min( maxScroll_left, scrollOptions.left ) );

    // let's begin
    window.scrollTo( scrollOptions );
    requestAnimationFrame( check );
    
    // this function will be called every painting frame
    // for the duration of the smooth scroll operation
    function check() {
      // check our current position
      const newPos_top = elem.scrollTop;
      const newPos_left = elem.scrollLeft;
      // we add a 1px margin to be safe
      // (can happen with floating values + when reaching one end)
      const at_destination = Math.abs( newPos_top - targetPos_top) <= 1 &&
        Math.abs( newPos_left - targetPos_left ) <= 1;
      // same as previous
      if( newPos_top === lastPos_top &&
        newPos_left === lastPos_left ) {
        if( same ++ > 2 ) { // if it's more than two frames
          if( at_destination ) {
            return resolve();
          }
          return reject();
        }
      }
      else {
        same = 0; // reset our counter
        // remember our current position
        lastPos_top = newPos_top;
        lastPos_left = newPos_left;
      }
      // check again next painting frame
      requestAnimationFrame( check );
    }
  });
}
#scroll_forcer {
  height: 5000vh;
  background-image: linear-gradient(to bottom, red, green);
  background-size: 100% 100px;
}
#scroll_forcer.scrolling {
  filter: grayscale(70%);
}
.as-console-wrapper {
  max-height: calc( 50vh - 30px ) !important;
}
<button id="trigger">click to scroll</button>
<div id="scroll_forcer">
</div>

【讨论】:

    【解决方案3】:

    希望对你有帮助

    <script type="text/javascript">
            jQuery(
              function ($) {
                  $(window).on("scroll", function () {
                      var scrollHeight = $(document).height();
                      var scrollPosition = $(window).height() + $(window).scrollTop();
                      if (scrollHeight - scrollPosition <= 180) {
                          // when scroll to bottom of the page
    
                             // your function to call
    
                      }
                  });
              }
            );
    </script>
    

    【讨论】:

      【解决方案4】:

      LINK的解决方案

      const onScrollStop = (callback) => {
      
              // Make sure a valid callback was provided
              if (!callback || typeof callback !== 'function') return;
      
              // Setup scrolling variable
              let isScrolling;
      
              // Listen for scroll events
              window.addEventListener('scroll', (event) => {
      
                  // Clear our timeout throughout the scroll
                  window.clearTimeout(isScrolling);
      
                  // Set a timeout to run after scrolling ends
                  isScrolling = setTimeout(() => {
      
                      // Run the callback
                      callback();
      
                  }, 66);
      
              }, false);
      
          };
      
          onScrollStop(() => {
              console.log('Scrolling has stopped.');
          });
      

      【讨论】:

        【解决方案5】:
        $(window).scroll(function(){
            if($(window).scrollTop() + $(window).height() >= $(document).height()) {
            //Your Stuff
        }
        });
        

        【讨论】:

        • 您好,欢迎来到 stackoverflow,感谢您的回答。虽然这段代码可能会回答这个问题,但您是否可以考虑添加一些解释来说明您解决了什么问题,以及您是如何解决的?这将有助于未来的读者更好地理解您的答案并从中学习。
        猜你喜欢
        • 2011-04-27
        • 2012-10-30
        • 2012-11-23
        • 1970-01-01
        • 2011-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多