<div class="virtual-list-item {{shown ? 'shown' : ''}}" virtual-list-key="{{key}}" ref:node style="transform: translateY({{offset}}px);" > <:Component {component} virtualProps="{{props}}" virtualIndex="{{index}}" virtualLength="{{$numItems}}" on:recalculateHeight="doRecalculateHeight()"/> </div> <style> .virtual-list-item { position: absolute; top: 0; opacity: 0; pointer-events: none; /* will-change: transform; */ /* causes jank in mobile Firefox */ } .virtual-list-item.shown { opacity: 1; pointer-events: auto; } </style> <script> import { virtualListStore } from '../_utils/virtualListStore' import { AsyncLayout } from '../_utils/AsyncLayout' const keyGetter = node => node.getAttribute('virtual-list-key') const asyncLayout = new AsyncLayout(keyGetter) export default { oncreate() { let key = this.get('key') asyncLayout.observe(key, this.refs.node, (rect) => { // update all item heights in one microtask batch for better perf this.store.batchUpdate('itemHeights', key, rect.height) }) }, ondestroy() { let key = this.get('key') asyncLayout.unobserve(key, this.refs.node) }, store: () => virtualListStore, computed: { 'shown': ($itemHeights, key) => $itemHeights[key] > 0 }, methods: { doRecalculateHeight() { let tempAsyncLayout = new AsyncLayout(keyGetter) let key = this.get('key') tempAsyncLayout.observe(key, this.refs.node, (rect) => { tempAsyncLayout.disconnect() // update all item heights in one microtask batch for better perf this.store.batchUpdate('itemHeights', key, rect.height) }) } } } </script>