• This repository has been archived on 01/Oct/2021
  • Stars
    star
    1,998
  • Rank 23,188 (Top 0.5 %)
  • Language
  • License
    Other
  • Created over 6 years ago
  • Updated about 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A built-in virtual scroller for the web platform

This repository hosts explorations for a new web platform feature, a virtual scroller control. The idea of a virtual scroller is to provide a scrolling "viewport" onto some content, allow extremely large numbers of elements to exist, but maintain high performance by only paying the cost for those that are currently visible. Traditionally, we say that the non-visible content is virtualized.

Current status

This project is no longer being actively developed. Instead we are pursuing primitives here.

Why a virtual scroller?

Virtualized content is a popular and important pattern on the web. Most content uses it in some form: the https://m.twitter.com and https://facebook.com feeds; Google Photos and YouTube comments; and many news sites which have an automatic "scroll to next article" feature. Most popular frameworks have at least one high-usage virtualization component, e.g. React Virtualized with ~290K weekly downloads. See the infinite list study group for more research on existing virtualization solutions, both on the web and in other platforms like iOS.

At the same time, virtual scrollers are complex and hard to get right. In fact, having a first-class experience with virtualized content is currently impossible, because browsers don't expose the right hooks: things like accessible landmark navigation, find in page, or intra-page anchor navigation are based solely on DOM structure, and virtualized content is by definition not in the DOM. Additionally, today's virtualized content does not work with search engine crawlers, which means that sites that care about search engine ranking are unable to apply this important performance technique. This is bad for the web.

We believe that, like native platforms, the web deserves a first-class virtual scroller implementation that works out of the box.

For more details on the motivation, see Motivation.md.

Sample code

<!--
  virtual-scroller lives in a built-in module and needs to be imported before use.
  (The name of the module is subject to change.)
-->
<script type="module">
import "std:virtual-scroller";
</script>

<!--
  The <virtual-scroller> will manage the rendering of its children.
  It will prioritize rendering things that are in the viewport and not render
  children that are far away, such that we are only paying as little rendering
  cost as possible while still allowing them to work with find-in-page,
  accessibility features, focus navigation, fragment URL navigation, etc.
-->
<virtual-scroller id='scroller'>
  <div>Item 1</div>
  <div>Item 2</div>
  ...
  <div>Item 1000</div>
</virtual-scroller>

<script>
// You can add, remove, modify children of the <virtual-scroller> as you would
// a regular element, using DOM APIs.
scroller.append(...newChildren);

// When the set of actually-rendered children is about to change,
// the <virtual-scroller> will fire a "rangechange" event with the
// new range of rendered children.
scroller.addEventListener('rangechange', (event) => {
  if (event.first === 0) {
    console.log('rendered first item.');
  }
  if (event.last === scroller.children.length - 1) {
    console.log('rendered last item.');
    // Perhaps you would want to load more data for display!
  }
});
</script>

Goals

  • Be flexible enough to be used for various kinds of scrolling, larger-than-viewport content, from news articles to typeaheads to contact lists to photo galleries.
  • Only pay the rendering costs for visible child elements of the scroller (with some margin).
  • Allow contents of the scroller to work with find-in-page, accessibility features, focus, fragment URL navigation, etc., just as they would in a non-virtualized context.
  • Be flexible enough to allow developers to integrate advanced features and behaviors, such as groupings, sticky headers, animations, swiping, item selection, etc.
  • Support 1D horizontal/1D vertical/2D wrapped-grid layouts.

Non-goals

  • Allow data that are not part of the DOM to work with find-in-page, accessibility, etc. The DOM remains the source of truth for these browser features, not e.g. the server-side, or other in-memory JavaScript data structures. See below for more elaboration.

And, at least in v1, the following are out of scope:

  • Built-in support for advanced features and behaviors, such as those mentioned above.
  • Support infinite-grid (spreadsheet-like), multi-column (content continues from bottom of col(N) to top of col(N+1)) or masonry layouts.

Proposed APIs

<virtual-scroller> element

The <virtual-scroller> element represents a container that will manage the rendering of its children. The children of this element might not get rendered/updated if they are not near or in the viewport. The element is aware of changes to the viewport, as well as to its own size, and will manage the rendered state of its children accordingly.

Its children can be any element. Semantically, the element is similar to a <div>, with the addition of focus on only the visible contents when the element overflows its container and is scrolled.

The sizes and other layout values of the non-rendered children, which might affect scrollbar height, etc., are approximate sizes and might not be always accurate. The rendered children always have accurate style and layout values, just like other normal DOM nodes.

All children, rendered or non-rendered, will work with find-in-page, focus navigation, fragment URL navigation, and accessibility technology, just like normal DOM nodes.

rangechange event

Fired when <virtual-scroller> is about to render a new range of items, e.g. because the user scrolled. This will fire at requestAnimationFrame timing, i.e. before the browser is about to paint.

The event has the following properties:

  • first: an integer, the 0-based index of the first children currently rendered.
  • last: an integer, the 0-based index of the last children currently rendered.
  • bubbles: false
  • cancelable: false
  • composed: false

As an example, this can be used to delay more costly rendering work. For example, a scrolling code listing could perform just-in-time syntax highlighting on lines right before they become visible, leaving the un-adorned code accessible by find-in-page/etc. but improving the code's appearance before the user sees it.

TODO: do we need first and last on the event, or should we just use the properties of the element?

rangeFirst and rangeLast getters

These return 0-based indices giving the first and last children currently rendered.

TODO: these names are kind of bad?

Constraints and effects

Ideally, we would like there to be zero constraints on the contents of the <virtual-scroller> element, or on the virtual-scroller element itself.

Similarly, we would like to avoid any observable effects on the element or its children. Just like how <select> does not cause its <option> elements to change observably when you open the select box, ideally <virtual-scroller> should not cause observable effects on its children as the user scrolls around.

This may prove difficult to specify or implement. In reality, we expect to have to add constraints such as:

  • Overriding the default values for certain CSS properties (and ignoring web developer attempts to set them).
  • Having degenerate behavior if visual order does not match DOM order (e.g. via flex-order or position: absolute).

And the control may influence its children via effects such as:

  • Changing the computed style of children (observable via getComputedStyle(child)).
  • Changing the display-locked status of children (observable via child.displayLock.locked).
  • Changing the layout of non-visible children's descendants (observable via e.g. child.children[0].getBoundingClientRect()).

Figuring out the exact set of constraints (including what happens when they're violated), and the exact set of effects, is a key blocker for standardization that we expect to address over time.

Use cases

Cases this proposal covers well

This design is intended to cover the following cases:

  • Short (10-100 item) scrollers. Previously, virtualizing such scrollers was done rarely, as virtualization caused sacrifices in developer and user experience. We are hopeful that with a first-class virtualization element in the web platform, it will become more expected to use <virtual-scroller> in places where overflow-scrolling <div>s were previously seen, thus improving overall UI performance.

  • Medium (100-10Β 000 item) scrollers. This is where virtual scrollers have traditionally thrived. We also want to expand this category to include not just traditional list- or feed-like scenarios, but also cases like news articles.

  • Large (10Β 000+ item) scrollers, where data is added progressively. As long as the data can be held in memory, <virtual-scroller> ensures that there are no rendering costs, and so can scale indefinitely. An example here would be any interface where scrolling down loads more content from the server, indefinitely, such as a social feed or a busy person's inbox.

    However, note that adding a large amount of data at once is tricky with this API; see below.

Very large amounts of initial data

Consider the cases of the singlepage HTML specification, or of long-but-finite lists such as a large company directory.

We believe that all these scenarios are still suited for use with this virtual scroller control. As long as the data could feasibly fit in memory in any form, the user experience will be best if it is stored in the DOM, inside a virtual scroller that makes the rendering costs of out-of-viewport items zero. This allows access to the data by browser features, such as find-in-page or accessibility tooling, as well as by search engines.

However, using the above API in these scenarios suffers from the problem of initial page load costs. Trying to server-render all of the items as <virtual-scroller> children, or trying to do an initial JSON-to-HTML client-render pass, will jank the page. For example, just the parsing time alone for the single-page HTML specification can take 0.6–4.4 seconds. And there are staging problems in trying to deliver large amounts of HTML while the "std:virtual-scroller" module is still being imported, which could prevent it from properly avoiding initial rendering costs.

As such we think there is still room for improvement in these scenarios, e.g. with an API that makes it easy to progressively stream data during idle time to allow the initial few screenfuls to render ASAP and without jank. We will be exploring this problem over time, after we feel confident that we can specify and implement a solution for the core use cases.

Almost-infinite data from the server

A consistent point of confusion about the virtual scroller proposal is how it purports to solve cases like social feeds, where there is an "almost infinite" amount of data available.

This proposal's answer is that: if you were going to have the data in memory anyway, then it should be in the <virtual-scroller>, and thus accessible to the browser or other technologies (such as search engines) that operate on the DOM. But, if you were going to leave the data on the server, then it is fine to continue leaving it on the server, even with a <virtual-scroller> in play.

For example, in one session while browsing https://m.twitter.com/, it limited itself to only keeping 5 tweets in the DOM at one time, using traditional virtualization techniques. However, it appeared to have about 100 tweets in memory (available for display even if the user goes offline). And, when the user began scrolling toward the bottom of the page, it queried the server to increase the amount of in-memory tweets it had available. With a native <virtual-scroller> in the browser, which mitigates the rendering costs while still allowing you to keep items in the DOM, we're hopeful that it'd be possible to keep those 100+ tweets as DOM nodes, not just in-memory JavaScript values that are locked away from find-in-page and friends.

This proposed design does mean that there could be things on the Twitter servers which are not findable by find-in-page, because they have not yet been pulled from the server and into the DOM. That is OK. Find-in-page is not meant to be find-in-site, and users of social feeds are able to understand the idea that not everything is yet loaded. What is harder for them to understand is when they saw a phrase, they scroll past it by 100 pixels, and then find-in-page can't see it anymore, because it's been moved out of the DOM. <virtual-scroller> addresses this latter problem.

Alternatives considered

Using traditional virtualization

Previously, we intended to specify a traditional approach to virtualization for the built-in virtual scroller. With that approach, the element would map JavaScript values ("items") to DOM element children, putting only a small portion of the items in the DOM, with callbacks for creating, updating, and recycling the DOM elements given an item.

However, this approach suffers the same problem as existing traditionally-virtualized scrollers regarding accessibility, find-in-page, fragment URL and focus navigation, etc., all of which depend on having the content be part of the DOM to work correctly. This is a known issue with traditional virtualization, which web developers have to grapple with today, trading off these functionalities with the performance improvement. As we intend for the built-in virtual scroller to be a standard building block that a lot of web authors would use or build on, we don't want to continue having this disadvantage.

In other words, given the problem of too much DOM causing bad performance, traditional virtualization is managing the symptoms, by decreasing the amount of DOM. For a standard solution, we want to tackle the core problem head-on.

Find-in-page APIs

As mentioned in the previous section, we want to make features like find-in-page work with the built-in virtual scroller. We have briefly considered adding a set of find-in-page APIs to the web platform, that would support cases like giving the web author a way to completely override the find-in-page command, or interacting with and adding results to the user agent's built-in find-in-page functionality.

However, designing these APIs proved to be quite challenging, given the breadth of find-in-page user interfaces across browsers. Worse, providing a find-in-page-specific solution might unintentionally become a disadvantage for other things like accessibility: web developers might be inclined to think that a virtual scroller that works with find-in-page is good enough, and not think about the remainder of the missing functionality caused by virtualization.

Libraries

Another approach would be to standardize and implement only the low-level primitives which allow mitigating the cost of DOM, i.e. display locking. We would then leave the building of high-level virtual scroller APIs to libraries.

We fully expect that some applications and libraries will take this route, and even encourage it when appropriate. But we still believe there is value in providing a high-level virtual scroller control built into the platform, for the 90% case. For more on our reasoning, see the motivation document's "Standardization" and "Layering" sections.

Sample implementations

Chrome

Launch chrome with flags --enable-blink-features=DisplayLocking,BuiltInModuleAll to get a working virtual-scroller element.

Demos

https://github.com/fergald/virtual-scroller-demos

More Repositories

1

webcomponents

Web Components specifications
HTML
4,360
star
2

import-maps

How to control the behavior of JavaScript imports
JavaScript
2,705
star
3

focus-visible

Polyfill for `:focus-visible`
JavaScript
1,607
star
4

webusb

Connecting hardware to the web.
Bikeshed
1,310
star
5

webpackage

Web packaging format
Go
1,231
star
6

EventListenerOptions

An extension to the DOM event pattern to allow authors to disable support for preventDefault
JavaScript
1,166
star
7

portals

A proposal for enabling seamless navigations between sites or pages
HTML
946
star
8

floc

This proposal has been replaced by the Topics API.
Makefile
934
star
9

inert

Polyfill for the inert attribute and property.
JavaScript
920
star
10

scheduling-apis

APIs for scheduling and controlling prioritized tasks.
HTML
909
star
11

view-transitions

811
star
12

file-system-access

Expose the file system on the user’s device, so Web apps can interoperate with the user’s native applications.
Bikeshed
658
star
13

background-sync

A design and spec for ServiceWorker-based background synchronization
HTML
639
star
14

ua-client-hints

Wouldn't it be nice if `User-Agent` was a (set of) client hints?
Bikeshed
590
star
15

scroll-to-text-fragment

Proposal to allow specifying a text snippet in a URL fragment
HTML
586
star
16

observable

Observable API proposal
Bikeshed
582
star
17

aom

Accessibility Object Model
HTML
567
star
18

kv-storage

[On hold] A proposal for an async key/value storage API for the web
550
star
19

turtledove

TURTLEDOVE
Bikeshed
526
star
20

navigation-api

The new navigation API provides a new interface for navigations and session history, with a focus on single-page application navigations.
Makefile
486
star
21

webmonetization

Proposed Web Monetization standard
HTML
461
star
22

trust-token-api

Trust Token API
Bikeshed
421
star
23

attribution-reporting-api

Attribution Reporting API
Bikeshed
360
star
24

direct-sockets

Direct Sockets API for the web platform
HTML
329
star
25

shape-detection-api

Detection of shapes (faces, QR codes) in images
Bikeshed
304
star
26

display-locking

A repository for the Display Locking spec
HTML
297
star
27

dbsc

Bikeshed
297
star
28

background-fetch

API proposal for background downloading/uploading
Shell
281
star
29

first-party-sets

Bikeshed
280
star
30

serial

Serial ports API for the platform.
HTML
256
star
31

resize-observer

This repository is no longer active. ResizeObserver has moved out of WICG into
HTML
255
star
32

priority-hints

A browser API to enable developers signal the priorities of the resources they need to download.
Bikeshed
249
star
33

sanitizer-api

Bikeshed
227
star
34

is-input-pending

HTML
221
star
35

proposals

A home for well-formed proposed incubations for the web platform. All proposals welcome.
216
star
36

spatial-navigation

Directional focus navigation with arrow keys
JavaScript
212
star
37

js-self-profiling

Proposal for a programmable JS profiling API for collecting JS profiles from real end-user environments
HTML
197
star
38

cq-usecases

Use cases and requirements for standardizing element queries.
HTML
184
star
39

isolated-web-apps

Repository for explainers and other documents related to the Isolated Web Apps proposal.
Bikeshed
182
star
40

visual-viewport

A proposal to add explicit APIs to the Web for querying and setting the visual viewport
HTML
177
star
41

interventions

A place for browsers and web developers to collaborate on user agent interventions.
176
star
42

frame-timing

Frame Timing API
HTML
170
star
43

layout-instability

A proposal for a Layout Instability specification
Makefile
158
star
44

page-lifecycle

Lifecycle API to support system initiated discarding and freezing
HTML
154
star
45

nav-speculation

Proposal to enable privacy-enhanced preloading
HTML
154
star
46

speech-api

Web Speech API
Bikeshed
145
star
47

cookie-store

Asynchronous access to cookies from JavaScript
Bikeshed
143
star
48

construct-stylesheets

API for constructing CSS stylesheet objects
Bikeshed
137
star
49

webhid

Web API for accessing Human Interface Devices (HID)
HTML
137
star
50

color-api

A proposal and draft spec for a Color object for the Web Platform, loosely influenced by the Color.js work. Heavily WIP, if you landed here randomly, please move along.
HTML
132
star
51

fenced-frame

Proposal for a strong boundary between a page and its embedded content
Bikeshed
126
star
52

devtools-protocol

DevTools Protocol
JavaScript
120
star
53

sms-one-time-codes

A way to format SMS messages for use with browser autofill features such as HTML’s autocomplete=one-time-code.
Makefile
111
star
54

bundle-preloading

Bundles of multiple resources, to improve loading JS and the Web.
HTML
105
star
55

translation-api

A proposal for translator and language detector APIs
Bikeshed
104
star
56

privacy-preserving-ads

Privacy-Preserving Ads
HCL
100
star
57

manifest-incubations

Before install prompt API for installing web applications
HTML
99
star
58

window-controls-overlay

HTML
97
star
59

netinfo

HTML
95
star
60

compression-dictionary-transport

94
star
61

intrinsicsize-attribute

Proposal to add an intrinsicsize attribute to media elements
93
star
62

animation-worklet

🚫 Old repository for AnimationWorklet specification ➑️ New repository: https://github.com/w3c/css-houdini-drafts
Makefile
92
star
63

container-queries

HTML
91
star
64

local-peer-to-peer

↔️ Proposal for local communication between browsers without the aid of a server.
Bikeshed
90
star
65

shared-storage

Explainer for proposed web platform Shared Storage API
Bikeshed
89
star
66

async-append

A way to create DOM and add it to the document without blocking the main thread.
HTML
87
star
67

indexed-db-observers

Prototyping and discussion around indexeddb observers.
WebIDL
84
star
68

canvas-formatted-text

HTML
82
star
69

file-handling

API for web applications to handle files
82
star
70

canvas-color-space

Proposed web platform feature to add color management, wide gamut and high bit-depth support to the <canvas> element.
79
star
71

local-font-access

Web API for enumerating fonts on the local system
Bikeshed
77
star
72

performance-measure-memory

performance.measureMemory API
HTML
77
star
73

digital-credentials

Digital Credentials, like driver's licenses
HTML
77
star
74

handwriting-recognition

Handwriting Recognition Web API Proposal
Bikeshed
75
star
75

web-app-launch

Web App Launch Handler
HTML
75
star
76

pwa-url-handler

72
star
77

ContentPerformancePolicy

A set of policies that a site guarantees to adhere to, browsers enforce, and embedders can count on.
HTML
72
star
78

starter-kit

A simple starter kit for incubations
JavaScript
72
star
79

css-parser-api

This is the repo where the CSS Houdini parser API will be worked on
HTML
71
star
80

close-watcher

A web API proposal for watching for close requests (e.g. Esc, Android back button, ...)
Makefile
71
star
81

eyedropper-api

HTML
69
star
82

idle-detection

A proposal for an idle detection and notification API for the web
Bikeshed
67
star
83

storage-foundation-api-explainer

Explainer showcasing a new web storage API, NativeIO
65
star
84

video-editing

65
star
85

uuid

UUID V4
63
star
86

client-hints-infrastructure

Specification for the Client Hints infrastructure - privacy preserving proactive content negotiation
Bikeshed
61
star
87

sparrow

60
star
88

private-network-access

HTML
58
star
89

element-timing

A proposal for an Element Timing specification.
Bikeshed
57
star
90

document-picture-in-picture

Bikeshed
56
star
91

video-rvfc

video.requestVideoFrameCallback() incubation
HTML
53
star
92

time-to-interactive

Repository for hosting TTI specification and discussions around it.
52
star
93

digital-goods

Bikeshed
50
star
94

soft-navigations

Heuristics to detect Single Page Apps soft navigations
Bikeshed
46
star
95

raw-clipboard-access

An explainer for the Raw Clipboard Access feature
44
star
96

storage-buckets

API proposal for managing multiple storage buckets
Bikeshed
43
star
97

pending-beacon

A better beaconing API
Bikeshed
43
star
98

admin

πŸ‘‹ Ask your questions here! πŸ‘‹
HTML
42
star
99

web-smart-card

Repository for the Web Smart Card Explainer
HTML
42
star
100

web-preferences-api

The Web Preference API aims to provide a way for sites to override the value for a given user preference (e.g. color-scheme preference) in a way that fully integrates with existing Web APIs.
Bikeshed
41
star