WICG / virtual-scroller

Other
2k stars 83 forks source link

Consider high-level "declarative" ways of providing items #129

Closed domenic closed 5 years ago

domenic commented 6 years ago

It would be nice if there were versions of virtual-scroller that didn't require using JavaScript to provide the items. One example:

<super-virtual-scroller>
  <div>1</div>
  <div>2</div>
  ...
  <div>1 million</div>
</super-virtual-scroller>

This version would be slower than one that only lazily created the HTML elements, but it would still be faster to use super-virtual-scroller than to use div, because the super-virtual-scroller would avoid all the rendering work for non-visible elements (even if you still paid the parsing costs).

Another variant:

<super-duper-virtual-scroller itemsource="mydata">
  <template>
    <div>{{item}}</div>>
  </template>
</super-duper-virtual-scroller>

<script type="application/json" id="mydata">
[
  "1",
  "2",
  ...,
  "1 million"
]
</script>

This JSON-based version is basically blocked on template instantiation, and in particular on a strong version of template instantiation that includes a default processor. So it's pretty far-future.

Comparing these two variants, it's notable that at least in some cases the inflation factor going from JSON to HTML can be very significant, e.g. one case we looked at had 3 MiB of JSON -> 27 MiB of HTML. So that's one reason why the extra complexity of the JSON version might still be worthwhile.


Ideally, it would be nice if we could make normal virtual-scroller flexible enough that these two sorts of things could be layered on top of it. I think that might be true today already? E.g. super-virtual-scroller might be something like this:

class SuperVirtualScroller extends VirtualScroller {
  constructor() {
    super();

    // the items are elements
    this.itemSource = new ItemSource({
      item: index => this.children[index],
      getLength: index => this.childElementCount
    });
    this.createElement = item => item;
    this.updateElement = item => item.style.display = "";
    this.recycleElement = item => item.style.display = "none";
  }

  // TODO: make this more robust and reactive to children changes
  // Maybe the best thing to do is to just use CSS and/or shadow DOM.
  connectedCallback() {
    for (const child of this.children) {
      child.style.display = "none";
    }
  }
}

We should probably test these, and maybe build in the super-virtual-scroller functionality (either as a new element with a realistic name, or as a mode of virtual-scroller, or...)

domenic commented 5 years ago

The new approach outlined in the explainer basically meets the first example in this post. The rest of this post is concerned with the item-based API, which we've moved on from. So, closing!