【问题标题】:Make "scrollLeft" / "scrollTop" changes not trigger scroll event listener使“scrollLeft”/“scrollTop”更改不触发滚动事件监听器
【发布时间】:2010-11-26 01:55:57
【问题描述】:

目前我的程序处于一个位置,它既可以监听用户滚动某个元素,又有时会自动滚动该元素。 (不是一个渐进的、漂亮的滚动,而是一个瞬间的跳跃。我发誓,这在上下文中是有道理的。)

如果通过设置 scrollLeft 或 scrollTop 完成滚动,有没有办法使滚动事件不触发?我的第一个想法是一个基本的开关,比如:

ignoreScrollEvents = true;
element.scrollLeft = x;
ignoreScrollEvents = false;

function onScroll() {
  if(ignoreScrollEvents) return false;
}

但由于事件不会立即触发(哎呀,duhh),这不是一个可行的解决方案。我可以尝试什么其他类型的答案?我也在使用 jQuery,如果有帮助的话。

【问题讨论】:

    标签: javascript jquery html


    【解决方案1】:

    最简单的通用方法?只需在事件处理程序中重置标志。您需要先检查您是否真的在更改值,否则不会触发事件 - 如Rodrigo notes,这里的一个好做法是将其分解为所谓的“setter”函数:

    function setScrollLeft(x)
    {
      if ( element.scrollLeft != x )
      {
        ignoreScrollEvents = true;
        element.scrollLeft = x;
      }
    } 
    
    ...
    
    function onScroll() 
    {
      var ignore = ignoreScrollEvents;
      ignoreScrollEvents = false;
    
      if (ignore) return false;
    
      ...
    }
    

    但是,根据您的需要,您可能已经将滚动位置存储在某处;如果是这样,只需更新并检查它作为您的标志。比如:

    element.scrollLeft = currentScrollLeft = x;
    
    ...
    
    function onScroll() 
    {
      // retrieve element, etc... 
    
      if (element.scrollLeft == currentScrollLeft) return false;
    
      ...
    }
    

    【讨论】:

    • 在我的代码中,我遇到了一个问题:有时会发生,例如 scrollLeft 已经为 0,然后我的代码也将其设置为 0。因此,没有触发滚动事件,并且下一个用户滚动被忽略。 (如果他们使用 Home、End 等滚动,这才是真正的问题。)但这绝对是一个接近的解决方案 - 我知道我在那个虚拟实现中遗漏了一些愚蠢的东西。谢谢!
    • 我想知道...那么您最好的选择是在设置标志之前检查您是否更改了值 - 如果不是,请不要设置它。您可能已经将此代码分解为一个单独的函数,但如果没有,那么这样做会更容易 - 有关此示例,请参阅 Rodrigo 的答案。
    【解决方案2】:

    二传手怎么样?

    var _preventEvent = false; // set global flag 
    
    function setScrollTop(amount) {
      _preventEvent = true; // set flag
      document.documentElement.scrollTop = amount * Math.random();
    }
    
    function setScrollLeft(amount) {
      _preventEvent = true; // set flag
      document.documentElement.scrollLeft = amount * Math.random();
    }
    
    // scroll event listener
    window.onscroll = function() {
      console.clear();
      
      if (_preventEvent) {
        _preventEvent = false; // reset flag
        return;
      }
      
      console.log('normal scroll');
    }
    html{ height:500%; width:500%; } 
    button{ position:fixed; }
    button + button{ top:50px; }  
    <button onclick=setScrollTop(1000)>Random scrollTop</button>
    <button onclick=setScrollLeft(1000)>Random scrollLeft</button>

    【讨论】:

      【解决方案3】:

      您可以尝试存储元素的偏移顶部,并在您进入 onScroll 时将其与 scrollTop 匹配:

      function onScroll(){
          var scrollTop = (document.documentElement.scrollTop || document.body.scrollTop),
          offsetTop = $('selector').offset().top;
          if(offsetTop > scrollTop){
            //user has not scrolled to element so do auto scrolling
          }
      }
      

      【讨论】:

        【解决方案4】:

        好吧,我的最终解决方案,基于 Shog9(不是我的真实代码,而是我的方法):

        var oldScrollLeft = element.scrollLeft;
        element.scrollLeft = x;
        if(element.scrollLeft != oldScrollLeft) {
          ignoreScrollEvents = true;
        }
        
        function onScroll() {
          if(!ignoreScrollEvents) performGreatActions();
          ignoreScrollEvents = false;
        }
        

        if 语句仅在 scrollLeft 值实际最终发生变化时设置忽略标志,因此从 0 设置为 0 等情况不会错误设置标志。

        谢谢大家!

        【讨论】:

        • 不确定。这里有什么协议?如果我使用部分答案,然后将其构建以达到完整答案,我是标记自己的还是其他的?
        • 已接受的答案已被编辑以包含此警告,因此它现在与此答案匹配 :) 酷!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-13
        • 2015-08-18
        • 1970-01-01
        • 2011-01-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多