DOM element position slow while scrolling + differ

2019-04-13 06:34发布

问题:

I'm having a little problem with Safari - When i want to update a DOM element's position on scroll event, Safari seems not to catch up with the changes (resulting in a jumpy lag effect). I checked it on other browsers (Chrome, FF, IE8+) and this seems to be specific to Safari.

I made a jsfiddle to illustrate my problem:

$("#container").on("scroll", function() {
    $("#slide").css({
        left: $("#container").scrollLeft() + "px"
    });

    var leftPos = 0;
    for(var i = 0; i < 2000 ; i++) {
        leftPos = $("#slide").css("left");
    }  
    $("#info").text(leftPos); 
});

http://jsfiddle.net/a4b86et3/2/

As you can see, I added an additional loop of DOM reading on each scroll to simulate "more operations going on" on each event occurrence, as this mechanism is a part of a bigger project, which contains many other DOM operations. (Notice, that this example works smooth everywhere except Safari)

Also, i used jQuery just for the convenience, the actual project uses pure js.

I managed to partially fixed the issue

by changing the left = x property to transform = translate3d(x,0,0), so the browser would use the GPU.

$("#container").on("scroll", function() {
    $("#slide").css({
        '-webkit-transform': 'translate3d(' + $("#container").scrollLeft() + 'px, 0, 0)'
    });

    var leftPos = 0;
    for(var i = 0; i < 1900 ; i++) {
        leftPos = $("#slide").css("left");
    }  
    $("#info").text(leftPos);

});

http://jsfiddle.net/a4b86et3/3/

However, sometimes I'm still experiencing a slight lag/glitching while scrolling.

But, what's more important, this fix doesn't affect the scrolling, when I'm using a mouse scroll or touchpad! While dragging the scrollbar works way better, using any of the above brings me back to my initial problem.

Any ideas why this happens and how to fix it?


tl;dr; - Safari is slow when changing element position on scroll; translate3d seems to not work properly when using mouse scroll/touchpad.

回答1:

I had the exact same issue, and it took me some time to figure out the fix. Here is an updated jsfiddle showing you how to resolve the issue in Safari: http://jsfiddle.net/a4b86et3/4/

function handle_scroll() {
  $("#slide").css({
    '-webkit-transform': 'translate3d(' + $("#container").scrollLeft() + 'px, 0, 0)'
  });

  var leftPos = 0;
  for(var i = 0; i < 1900 ; i++) {
    leftPos = $("#slide").css("left");
  }  
  $("#info").text(leftPos);
}

$("#container").on("scroll", handle_scroll);
$("#container").on('mousewheel', function(e) {
  e.preventDefault()

  var maxX = this.scrollWidth  - this.offsetWidth
  var maxY = this.scrollHeight - this.offsetHeight
  this.scrollTop  = Math.max(0, Math.min(maxY, this.scrollTop  - e.originalEvent.wheelDeltaY))
  this.scrollLeft = Math.max(0, Math.min(maxX, this.scrollLeft + e.originalEvent.wheelDeltaX))

  handle_scroll(e)
})

In brief: if you handle the mousewheel event yourself, calculate the correct scroll values there, and then call your handler code, things start working.

I'm not sure why Safari makes you jump through this hoop, but luckily the fix isn't too involved.

Hope this helps.