javascript - 在分配滚动监听器之前更新 scrollTop 会触发滚动事件

标签 javascript

我正在开始监听滚动事件之前更新元素的 scrollTop。但是,滚动事件会触发,即使它是在 scrollTop 更新后 添加的。

我注意到,如果我将 .onscroll 赋值包装在 setTimeout(..., 1); 中,它工作得很好。

还要注意在输出中,内存中的 scrollTop 值没有改变,但它触发了事件。

starting out 0

haven't added listener yet 100

listener added 100

scrolled 100

谁能解释为什么会这样?使用 setTimeout 或设置标志作为解决此问题的方法似乎很老套;有没有更好的办法?

var div = document.getElementsByTagName('div')[0];

console.log("starting out", div.scrollTop);
div.scrollTop = 100;

console.log("haven't added listener yet", div.scrollTop);
div.onscroll = function() {
  console.log('scrolled', div.scrollTop);
};
console.log("listener added", div.scrollTop);
div {
  width: 80%;
  height: 200px;
  margin: 20px auto;
  overflow: auto;
  padding: 20px;
  box-sizing: border-box;
  border: 1px solid black;
}
<div>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi id mi sit amet tortor suscipit sagittis at id risus. Nullam ultricies nisi ac tortor ultrices porta. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nam purus sapien, efficitur in risus non, cursus egestas lectus. Curabitur pharetra erat sapien, et malesuada neque mollis nec. Donec dolor lacus, pretium eu ipsum sit amet, placerat vehicula orci. Sed viverra metus id magna sodales, et condimentum urna sagittis. Donec a viverra urna. Pellentesque lacinia commodo ligula sed porttitor. Aliquam iaculis diam nec nibh congue congue. Nunc eget dapibus orci, ac tristique nunc. Mauris nisl tellus, posuere sed lectus sit amet, vulputate sollicitudin magna. Nullam porttitor leo bibendum, varius libero vitae, ultrices diam. Donec mauris nulla, egestas non nisi sit amet, ultricies laoreet sapien.</p> 
<p>Vivamus mollis placerat felis ut porta. Pellentesque pellentesque blandit leo, fermentum sollicitudin risus porta quis. Phasellus gravida justo nec mi accumsan, in euismod tortor venenatis. Donec porttitor consequat dui ac iaculis. Quisque scelerisque dictum risus, eu gravida nibh sodales in. Vivamus sit amet consectetur urna. Aliquam quis pretium turpis, non rhoncus lectus. Ut vel mi urna. Mauris interdum congue felis ut faucibus. Praesent nec lobortis enim. Vestibulum velit nisl, dapibus vulputate interdum vitae, sagittis a nisl.</p> 
<p>Donec consectetur justo a purus sodales, quis ultrices enim sodales. Sed fermentum congue enim vehicula volutpat. Proin pellentesque elit et dolor congue, in blandit tellus aliquet. Cras tincidunt metus lorem, et tincidunt arcu condimentum eget. Curabitur sed ipsum nec erat mollis volutpat eget eget purus. Nam nec eleifend est. Sed ut elit eget odio mollis dignissim vitae nec urna. In vel libero eget libero rutrum consectetur non eget dui. Proin dignissim convallis elit, id mollis metus sodales eget. Aliquam non iaculis justo. Aenean vel diam nibh. Nam euismod viverra ante, ac molestie sem. Donec cursus justo a sagittis iaculis. Sed at maximus lacus, sit amet gravida mauris. Curabitur non odio at ipsum consequat fermentum. Morbi vestibulum nec dui id tincidunt.</p> 
<p>Integer nec nunc ultricies, mattis leo sed, gravida enim. In hac habitasse platea dictumst. Nunc sed turpis sed nisi consequat faucibus. Fusce sagittis maximus luctus. Maecenas at tortor blandit, imperdiet ligula vel, vestibulum diam. Proin consequat sodales nisl, quis varius erat semper vitae. Etiam ac pretium lacus. Phasellus in vestibulum tellus, nec tempus dolor. Etiam fringilla convallis rhoncus. Sed id enim erat. Integer congue orci sapien, ac porttitor arcu pellentesque eget. Mauris eu rutrum urna. Donec ante eros, scelerisque id ipsum et, pulvinar dapibus erat.</p> 
<p>Fusce eget bibendum eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Donec finibus justo a neque hendrerit, imperdiet fringilla nibh tincidunt. Etiam consequat a erat fringilla volutpat. Proin porttitor nec sem non semper. Suspendisse sollicitudin erat eu urna gravida volutpat quis sit amet leo. Mauris luctus purus eget purus mattis semper. Curabitur ut ante fringilla, porta neque non, hendrerit diam. Morbi dignissim congue orci ac vestibulum. Vestibulum ipsum urna, faucibus nec venenatis eu, efficitur vitae massa. Suspendisse lectus lorem, molestie vel consequat eget, malesuada sed turpis. Curabitur sit amet ipsum et justo sollicitudin pretium. Praesent dignissim, mauris ut hendrerit egestas, massa orci cursus justo, non porta metus erat et velit. Curabitur vitae orci eu erat ultrices aliquet.</p> 
</div>

最佳答案

我会说这是因为浏览器直到下一帧才进行实际滚动。因此事件监听器在滚动更新之前设置。

要避免这种情况,您可以做的是忽略第一个滚动事件。

var scrolled = false;

window.scrollTo(0, 100);

window.addEventListener('scroll', function() {
    if(!scrolled) {
        scrolled = true;
        return;
    }

    // Do stuff...
});

编辑以反射(reflect) OP 的评论:

我无法想象为什么这对你不起作用:

window.scrollTo(0, 100);

window.addEventListener('scroll', function() {
    if(typeof window.scrollEventHasFired === "undefined") {
        window.scrollEventHasFired = true;
        return;
    }

    // Do stuff...
});

进一步编辑,反射(reflect)新信息:

这对你有用吗?这使得可以设置多个事件,还允许在事件之外调用监听器: var 滚动 = [假], id = 0;

var listener = function(skipFirstExec, listenerId, args...) {
    if(skipFirstExec && !scrolled[listenerId]) {
        scrolled[listenerId] = true;
        return;
    }
    // Do stuff
};

window.addEventListener('scroll', listener.bind(thisArg, true, ++id));
scrolled[id] = false;

window.addEventListener('scroll', listener.bind(thisArg, false, ++id)); // This one won't skip the scroll
scrolled[id] = false;

window.addEventListener('scroll', listener.bind(thisArg, true, ++id));
scrolled[id] = false;

listener(false, 0, args...); // call listener outside of event

最终编辑:

这对我有用,不知道为什么我没有早点想到这个:

/**
 * requestAnimationFrame polyfill by Erik Möller & Paul Irish et. al.
 * https://gist.github.com/1866474
 *
 * http://paulirish.com/2011/requestanimationframe-for-smart-animating/
 * http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
**/

/*jshint asi: false, browser: true, curly: true, eqeqeq: true, forin: false, newcap: true, noempty: true, strict: true, undef: true */

(function( window ) {

  'use strict';

  var lastTime = 0;
  var prefixes = 'webkit moz ms o'.split(' ');
  // get unprefixed rAF and cAF, if present
  var requestAnimationFrame = window.requestAnimationFrame;
  var cancelAnimationFrame = window.cancelAnimationFrame;
  // loop through vendor prefixes and get prefixed rAF and cAF
  var prefix;
  for( var i = 0; i < prefixes.length; i++ ) {
    if ( requestAnimationFrame && cancelAnimationFrame ) {
      break;
    }
    prefix = prefixes[i];
    requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];
    cancelAnimationFrame  = cancelAnimationFrame  || window[ prefix + 'CancelAnimationFrame' ] ||
                              window[ prefix + 'CancelRequestAnimationFrame' ];
  }

  // fallback to setTimeout and clearTimeout if either request/cancel is not supported
  if ( !requestAnimationFrame || !cancelAnimationFrame ) {
    requestAnimationFrame = function( callback, element ) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
      var id = window.setTimeout( function() {
        callback( currTime + timeToCall );
      }, timeToCall );
      lastTime = currTime + timeToCall;
      return id;
    };

    cancelAnimationFrame = function( id ) {
      window.clearTimeout( id );
    };
  }

  // put in global namespace
  window.requestAnimationFrame = requestAnimationFrame;
  window.cancelAnimationFrame = cancelAnimationFrame;

})( window );
(function() {
    var div = document.getElementsByTagName('div')[0];

    console.log("starting out", div.scrollTop);
    div.scrollTop = 100;

    console.log("haven't added listener yet", div.scrollTop);
    window.requestAnimationFrame(function() {
        div.onscroll = function() {
          console.log('scrolled', div.scrollTop);
        };
        console.log("listener added", div.scrollTop);
    });
})();

关于javascript - 在分配滚动监听器之前更新 scrollTop 会触发滚动事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34299791/

相关文章:

javascript - 如何通过回车键验证答案?

javascript - innerHTML 导致 IE6(永久)锁定

javascript - 暴力破解字符串匹配javascript

javascript - 项目和总计的 jQuery 发票计算

javascript - 传递给子组件的 Vue.js Prop 对象未定义

javascript - 多窗口事件 - Jquery

javascript - 通过标记或 JS 强制下载

javascript - 在 Javascript 中执行多个 ajax 调用

javascript - 是否可以通过 Node.js 后端隐藏 React 组件源代码?

javascript - 在下拉菜单中设置默认值