Bug 206228 - element.style position DOM updates are laggy / not in sync
Summary: element.style position DOM updates are laggy / not in sync
Status: NEW
Alias: None
Product: WebKit
Classification: Unclassified
Component: Layout and Rendering (show other bugs)
Version: Safari 13
Hardware: Macintosh macOS 10.15
: P2 Normal
Assignee: Nobody
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-01-14 05:40 PST by cc.glows
Modified: 2020-01-14 14:32 PST (History)
3 users (show)

See Also:


Attachments
Demonstration of the problem (2.77 MB, video/quicktime)
2020-01-14 05:40 PST, cc.glows
no flags Details
Testcase (809 bytes, text/html)
2020-01-14 14:25 PST, Simon Fraser (smfr)
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description cc.glows 2020-01-14 05:40:45 PST
Created attachment 387649 [details]
Demonstration of the problem

Minimal Example:

```html
<style>
  body {
    height: 5000px;
  }

  .popper {
    position: absolute;
    width: 100px;
    height: 100px;
    background: red;
  }

  #scrollContainer {
    position: relative;
    width: 300px;
    height: 300px;
    background: gray;
    overflow-y: scroll;
    left: 200px;
  }

  #scrollContainer::after {
    content: '';
    display: block;
    width: 1px;
    height: 900px;
  }
</style>

<div id="windowElement" class="popper"></div>

<div id="scrollContainer">
  <div id="scrollingContainerElement" class="popper"></div>
</div>

<script>
  window.addEventListener('scroll', () => {
    windowElement.style.top = `${window.pageYOffset}px`;
  });

  scrollContainer.addEventListener('scroll', () => {
    scrollingContainerElement.style.top = `${scrollContainer.scrollTop}px`;
  });
</script>
```html

When scrolling the main window/body, the element is not updated synchronously and "lags" behind its correct position. The one inside the scrolling container however is updated correctly. On iOS, both of these are laggy.

A popular overlay (tooltip, popover, drop-down) positioning engine Popper.js - https://popper.js.org/ - relies on this behavior being 1:1 (in-sync), otherwise unsightly lagging is visible. `position: fixed` has its own problems in other scenarios, so it cannot be used.
Comment 1 Simon Fraser (smfr) 2020-01-14 13:05:48 PST
Scroll events are, per spec, asynchronous. Other browsers do a better job of sending them in sync with scrolling than WebKit, but you should never rely on them to reposition things. Use position:sticky, position:fixed etc instead.
Comment 2 cc.glows 2020-01-14 13:55:08 PST
(In reply to Simon Fraser (smfr) from comment #1)
> Scroll events are, per spec, asynchronous. Other browsers do a better job of
> sending them in sync with scrolling than WebKit, but you should never rely
> on them to reposition things. Use position:sticky, position:fixed etc
> instead.

If I wrap the updates in `setInterval()` without any duration argument, so it updates as fast as possible, the problem remains the same.
Comment 3 cc.glows 2020-01-14 14:20:35 PST
Just want to point out as well that this library has 4M downloads/week on npm, and billions of hits across CDNs - it solves an important problem in web applications that many people rely on. It's used in the biggest UI libraries (Bootstrap and Material UI) and needs to constrain the position of the element within the viewport so it's visible for the user as best as possible. Tons of developers are using this library right now in production, and millions upon millions of users are experiencing it.

Its entire purpose is to reposition an element near another one in sync with the layout. The "Overlow Prevention" example in the website link is an example of this, and the lag in Safari defeats this purpose (which seems unrelated to async scroll events - which should(?) not show the same effect with setInterval).

Even if the problem is with async scrolling, perhaps the spec should be ignored or updated to account for this problem. The positioning property cannot be updated to `fixed/sticky` at will either due to a host of other problems it causes regarding clipping containers, and massively complicates library logic due to the need to switch based on a variety of conditions.
Comment 4 Simon Fraser (smfr) 2020-01-14 14:25:31 PST
Created attachment 387703 [details]
Testcase
Comment 5 Simon Fraser (smfr) 2020-01-14 14:25:48 PST
It hiccups in Chrome too sometimes.
Comment 6 cc.glows 2020-01-14 14:32:00 PST
The stutters in Chrome aren't great, but they seem to rarely happen (and for at most 1 frame) and match Safari's behavior inside scrolling containers. In the case of the window in Safari, the lag is much more severe, and it even seems to "transition" to the position rather than be at 1:1 in position as expected.

I may make an issue for the slight stutters in Chromium as well.