Fixing the scrollTop
bug
The CSSOM View specification has a handful of widely-implemented properties on the Element
interface related to scrolling: scrollTop
, scrollLeft
, scrollWidth
, scrollHeight
. These give the current scroll position of the element, and the size of the scrolling area. scrollTop
and scrollLeft
can also be set to cause a scroll. When these are used on the root element, they instead reflect the scroll position and scroll area of the viewport. The clientWidth
and clientHeight
properties similarly reflect the viewport dimensions instead of the element’s dimensions for the root element.
However, WebKit (Safari), EdgeHTML and, previously, Chromium (Chrome before 61, Opera before 48) do not behave this way. They make the body
element reflect the viewport instead, and the root element returns 0
and does nothing on setting. IE11 and Firefox follow the specification. This is an interoperability problem that Web developers have to work around in some way, possibly by UA sniffing.
This bug has now been fixed in Chromium, but we still need your help to fix sites that rely on the bug based on UA sniffing.
Problematic cases
The problem is code that expects document.body
to be the “scrolling element”, either for all browsers or in certain browsers:
- Directly using
document.body.scrollTop
ordocument.body.scrollLeft
. This will already be broken (except in quirks mode) in Firefox, as well as Chrome 61 and later and Opera 48 and later. - User Agent-sniffing to decide between
document.body
ordocument.documentElement
. This will likely be broken in Chrome 61 and later and Opera 48 and later, and will possibly break in Safari and Edge in the future.
Recommendation
Use document.scrollingElement
if supported, and fall back to the current code.
For example, if the current (problematic) code is something like:
function bodyOrHtml() => {
if (navigator.userAgent.indexOf('WebKit') != -1) {
return document.body;
}
return document.documentElement;
}
...
bodyOrHtml().scrollTop = 100;
Then do something like this instead:
function bodyOrHtml() => {
if ('scrollingElement' in document) {
return document.scrollingElement;
}
// Fallback for legacy browsers
if (navigator.userAgent.indexOf('WebKit') != -1) {
return document.body;
}
return document.documentElement;
}
...
bodyOrHtml().scrollTop = 100;
Reporting issues
If you notice that a JS library uses UA sniffing to get “the scrolling element”, please file an issue in Chromium’s bug tracker. Include “ScrollTopLeftInterop” in the summary. Thank you!