8

I stumbled into this one whilst trying to scroll an element without invoking the normal event handlers

Using both Firefox and IE10 I'm seeing some really strange behaviour in how the scrollTop method is operating. For example, if I set the scrollTop on a div, and aferwards, bind a scroll event handler to the same element, the handler fires immediately. From my testing, this doesn't happen with Chrome, which leads me to think that FF and IE are applying the most miniscule of animations to their scrolls, or this is some kind of bug.

See JSFiddle example. Interestingly, if I set a timeout of 1ms before the assignment, the problem goes away. I'd love to know what's going on here, and what the best approach is to fix it.

Update: From the comments below it seems as though this might be recognised to be normal browser behaviour, so I'll update my question to ask what is going on here - please cite some interesting articles which explain this process in more detail.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Ian Clark
  • 9,237
  • 4
  • 32
  • 49
  • The whole JS code is executed before control is handed over to the browser UI again – setTimeout breaks that. – CBroe Nov 04 '13 at 11:35
  • OK, then why is the behaviour different for different browsers, and what can be done to fix it? – Ian Clark Nov 04 '13 at 11:44
  • Because: Different browsers are _different_ browsers. And as such, might implement this stuff differently. And timeouts _are_ a proven way to hand control back to the UI before executing other code. – CBroe Nov 04 '13 at 11:47
  • No bugs, just different implementations. Looks like IE10 behaves like Chrome when scrolling with mouse wheel, i.e. function is executed tick by tick, but the event fires continuosly, when using the scroll bar itself. To similarize behaviours, you could apply this [BGerrissen's answer](http://stackoverflow.com/a/4298672/1169519), though it's using timeouts too. – Teemu Nov 07 '13 at 17:02

1 Answers1

9

What goes on in IE and FF is the following:

  1. The scrollTop property is adjusted
  2. The scroll event handler is added
  3. Execution of the function finishes. Now, the browser has time for other things and will render the page. The rendering causes both the scroll handler and the scrollTop property to be committed simultaneously. The scrollTop change thus triggers a scroll event which is captured by your handler.

This is unlike this code:

var dv = document.getElementsByTagName("div")[0];
dv.scrollTop = dv.scrollHeight;
setTimeout(function(){
    dv.onscroll = function() { 
    console.log("scrolled!");
}, 0);

Where the following happpens:

  1. The scrollTop property is adjusted
  2. The setTimeout function appends the function that adds the onscroll event handler to the event queue. Essentially deferring the execution of the code until timeout processing occurs (see also the newer window.setImmediate).
  3. Execution of your function finishes. Now, the browser has time for other things and will render the page. The rendering will trigger a scroll event, but because your function was deferred, it has not yet been added and thus nothing captures the scroll event.
  4. Rendering the page finishes. Now, the browser has time for other things and will execute the function set by setTimeout. This will add the onscroll event handler, but since the scroll event has already been triggered, the event handler will not be called.

More information on the subject can be found here and here.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Borre Mosch
  • 4,404
  • 2
  • 19
  • 28
  • +1 for the first link. I found that a very interesting read, thank you. I find your actually answer quite tricky to understand however. – Ian Clark Nov 08 '13 at 14:56
  • It boils down to the fact that without setTimeout, the browser first attaches the event handler and then sends the event, and with setTimeout this is the other way around. This is because setTimeout will delay setting the event handler until after the rendering is done. – Borre Mosch Nov 08 '13 at 15:00
  • @Tegeril, thanks for the edits - I think it's a clearer read now – Ian Clark Nov 13 '13 at 10:03
  • Thank you both for the contributions, the first link in particular I found a very good read. – Ian Clark Nov 13 '13 at 22:20