w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.5k stars 661 forks source link

[css-scrollbars][proposal] Add support for attaching native scrollbars to an element #3397

Open andyearnshaw opened 5 years ago

andyearnshaw commented 5 years ago

Edit: I've put a more detailed proposal together here. Please give feedback, add a star, etc, if you are interested in the proposal.


There's currently no way to attach native scrolling capabilities to an element that does not overflow with other HTML content, for example a canvas element or an element that implements custom scrolling behaviour. There are plenty of custom scrolling solutions out there, but these are generally inferior or inconsistent with native scrollbars. For instance, an element with no natural overflow does not receive scrolling events on mobile devices, "elasticity" and momentum scrolling add a lot of complexity to a component. Custom scrollbars are also difficult to style like native scrollbars, forcing you to either use a style that is not consistent with native scrollbars on the page, or implementing themes for as many operating system/browser combinations that you care about.

A real world example is Google Sheets, which implements a custom scrollbar as the whole sheet is rendered on a <canvas> element for optimisation. My own use case is similar, I'm working on a grid that can potentially have hundreds of thousands of rows and/or columns, which reuses DOM elements rather than overflowing the container. We use a custom scrollbar to deal with this, but the issues around it are nontrivial to deal with.

My initial thoughts are to introduce a Scrollbar interface, and a new method, Element.prototype.attachScrollbar(options), which returns an instance of the Scrollbar

enum ScrollbarAxis { "horizontal", "vertical" };

[Exposed=Window]
interface Scrollbar : EventTarget {
  readonly attribute ScrollbarAxis axis;
  attribute unrestricted double scrollValue;
  attribute unrestricted double scrollMax;

  void addListener(EventListener? listener);
  void removeListener(EventListener? listener);
           attribute EventHandler onchange;
};

dictionary ScrollbarOptions {
  ScrollbarAxis axis = "vertical";
};

partial interface Element {
  Scrollbar attachScrollbar(optional ScrollbarOptions options)
};

Attaching to an element that already has a scrollbar would result in that element's scrollbar being hidden and the declared one to be used in its place. This would allow you to do things like render a scrollbar that does not represent the element's true overflow -- like having a smaller thumb and a larger track area where "infinite" scrolling is implemented, for example. The element would then become a scroll target, with the scrollbar dispatching cancellable change events when the element is scrolled by the user. Ideally, the scrollbar's top/bottom or left/right positions could be defined via new css properties, so that it can be positioned between any fixed content.

I'm happy to do additional legwork here, such as writing the spec, if there is implementor interest.

upsuper commented 5 years ago

I have a feeling that your usecase isn't terribly hard to achieve with native scrollbars. Some proof-of-concept prototype: https://jsfiddle.net/upsuper/yvhLdf71/ so it's not clear to me whether this is something worth adding to the platform.

jonjohnjohnson commented 5 years ago

@upsuper I'm not exactly arguing for this issue, but webkits implementation of sticky positioning does render your example quite insufficient, with of their reliance on "rubberbanding" at scroll boundaries. Pending they ship overscroll-behavior.

andyearnshaw commented 5 years ago

@upsuper that's an interesting proof of concept, and I will certainly experiment with it to see if it works for us, but it feels somewhat like a hack and not really what sticky was intended for.

andyearnshaw commented 5 years ago

@upsuper since you have experience working on scrolling in Firefox, can you tell me if there would be any issues arising from the setting the height of that inner element to ludicrous sizes? For example, say my grid has 1,000,000 rows (we have clients that are not far off this) at an average of 32px per row, making the scroll height 32,000,000px. Would the height of this element cause any performance degradation whilst scrolling? What about memory usage?

andyearnshaw commented 5 years ago

Okay, was able to do some testing over the weekend to partially answer my own question. Chrome and Safari have a max element height or width of about 33.5 million pixels, Firefox about 18 millions pixels and Edge about 1.5 million pixels. This would fit in 1 million rows at 32px per row, just, but if our default row height changes or we get a customer that goes over that 1 million, we'd be pushing it. A bit of math to work around that might help, but this highlights the "hackiness" of this solution.

andyearnshaw commented 5 years ago

Here are some implementations using variations of @upsuper's suggestion, along with their respective issues for the maximum element size limit mentioned in my previous post:

https://github.com/bvaughn/react-virtualized ★ 14,024 (no issue, but they are working around the problem by virtualising the scroll value using the same technique I mentioned previously, which has issues with scrolling precision) https://github.com/mleibman/SlickGrid/ ★ 6,478 (issue) https://github.com/NeXTs/Clusterize.js/ ★ 6,068 (issue) https://github.com/rintoj/ngx-virtual-scroller ★ 558 (issue) https://github.com/stackfull/angular-virtual-scroll ★ 262 (issue) https://github.com/tbranyen/hyperlist ★ 185 (no issue for maximum element size—yet)

The use cases are out there for "virtualized scrolling", these repos demonstrate that a native solution would be well-received. Google is littered with further examples and attempts, though few hit the limitation in their testing.

andyearnshaw commented 5 years ago

I've created a gist outlining the proposal in more detail. I realise this isn't a burning issue for most web developers, but virtual scrolling is something that is widely used on the web and it would be great to move this proposal forward.

daKmoR commented 5 years ago

did you saw https://github.com/valdrinkoshi/virtual-scroller I think this is also an idea to gather info for an actual spec

andyearnshaw commented 5 years ago

Interesting, I hadn't come across that, although the layered API project on which it is intended to become a part of sounds familiar, so I may have come across it before and just forgotten about it.

The layered API is supposed to be for high level features that do not provide capabilities that you can't get with low level features, but because of the limitation outlined in my gist (and discovered by the virtual-scroller author) , it's not possible to create reliably without a new low level feature. As such, this proposal would serve as a new low level feature that virtual-scroller would build on top of.

andyearnshaw commented 5 years ago

Here's some feedback from one of the Chromium developers about increasing the maximum element sizes:

Realistically we can't increase the max size without severely regressing memory usage across the board. The fact that roughly the same limit applies to multiple implementations reinforces this tradeoff.

So it looks like there may be no way forward to solving this problem without going through the specs.