使“scrollLeft”/“scrollTop”更改不触发滚动事件监听器

Posted

技术标签:

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

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

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

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

function onScroll() 
  if(ignoreScrollEvents) return false;

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

【问题讨论】:

【参考方案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 等情况不会错误设置标志。

谢谢大家!

【讨论】:

不确定。这里有什么协议?如果我使用部分答案,然后将其构建以达到完整答案,我是标记自己的还是其他的? 已接受的答案已被编辑以包含此警告,因此它现在与此答案匹配 :) 酷!

以上是关于使“scrollLeft”/“scrollTop”更改不触发滚动事件监听器的主要内容,如果未能解决你的问题,请参考以下文章

jQuery 源码解析(二十八) 样式操作模块 scrollLeft和scrollTop详解

scrollTop 和 scrollLeft 在 display:none 元素上不起作用

JQUERY中的scrollTop 和scrollleft到底是啥意思

使用动画链(jQuery)在 Ipad 上的 ScrollLeft 和 ScrollTop

使用 -webkit-overflow-scrolling:touch - Safari iOS javascript 事件 (scrollTop / scrollLeft) 时的当前滚动位置

终于搞懂scrollTop, offsetTop, scrollLeft, offsetLeft......