Chrome vertical jump when refreshing page scrolled

2019-06-17 23:16发布

问题:

In noticed the following behavior when developing a website using Chrome: when I refresh the page while it's being scrolled fully to bottom I can observe a vertical jump.

See the following Bootply.

To reproduce, open the fullscreen preview (the monitor icon on the right), and try the following:

  1. refresh the page (confirm form resubmission) --> no jump
  2. scroll to middle, refresh (confirm form resubmission) --> no jump
  3. scroll to very bottom, refresh (confirm form resubmission) --> vertical jump

The jump is in fact caused by this Javascript that tries to maintain vertical rhythm when page contains figures with .align-center class:

$(document).ready(function() {
  $(window).resize(function() {
    var baseline = parseInt($('body').css('line-height'), 10)
    $('.align-center').each(function() {
      var height = $(this).outerHeight();
      console.log(height)
      var bottom = baseline - (height % baseline);

      if (bottom != 0)
      {
        bottom += parseInt($(this).css('padding-bottom'), 10)
        $(this).css('padding-bottom', bottom);
      }
    });
  }).trigger("resize");
});

Of course removing this Javascript also removes the vertical jump observed. What I don't understand is that padding is applied when DOM is ready so it shouldn't cause visible vertical jumps. I think the jump has to do with the way Chrome handles the viewport when page is scrolled to very bottom but I don't really know how to confirm/infirm this.

When trying this in Firefox or Safari, I don't observe any jump.

Any idea please?


Edit: I opened a bug on Chrome's bug tracker.

回答1:

I do not have a complete answer but some observations regarding Chrome:

  1. Browser can start painting the document before document ready event
  2. Browser can show vertical scrollbar before document ready event
  3. Browser can scroll to the previously visible content before document ready event
  4. Browser can scroll to the previously visible content after window load

Your code seems to add a 20px padding to the figure on document ready event. So here are the consequences:

Scroll to top or middle + Refresh:

The browser scrolls the same content into view that was visible before the refresh. The increase in body height does not affect the scroll position except that the scrollbar changes its height.

Scroll to bottom + Refresh:

The browser tries to align the body to the bottom when possible. It seems to do this twice+. The body is scrolled all the way to the bottom when the content is available. Then 20px is added to the body height on document ready which activates the "scroll down" button. On page load, the browser aligns the body with the bottom again, pushing all the content down by 20px which creates the "vertical jump" behavior.

+ For testing I added $(window).scroll(function() { console.log(arguments); }). The scroll event fired twice: after document read and after window load.

In summary:

Chrome seems to align the body with the bottom of the page if it was that way before page refresh. And it does so pre-maturely and after the page loads causing the jumping effect.

Firefox seems to follow the same steps. However, it seems like Firefox handles the scroll to bottom case intelligently. Since the body is aligned with the bottom, it makes the layout changes (triggered by padding) in the area above the viewport; increasing the height of scrollbar but not scrolling.



回答2:

First of all, I have to disappoint you, as I'm not a Chrome dev or other official source. But I did put some time into the issue and thought I'd share what I found. You know, for posterity.

As you probably have, I put a couple alerts in the code and tried to observe, what exactly was happening. It seems to me like the viewport is rendered, the Javascript runs, the padding is applied, the entire rest of the content is pushed down, then Chrome realizes and fixes the viewport height to accomodate the additional padding. It looks like an honest rendering bug to me.

That said, I did find something that fixed the effect, at least in my tests. I don't know whether this is applicable in your environment, but maybe it does help in diagnosing the rendering issue further.

Simply adding the additional space to a margin, instead of the padding, did it for me.

if (bottom != 0)
{
    bottom += parseInt($(this).css('padding-bottom'), 10);
    $(this).css('margin-bottom', bottom);
}

Maybe somebody else can come up with an actual explanation. I'd sure like to know what exactly causes this unnerving behavior.