JSFiddle - React, Tailwind, and code Playground
Vue Virtual Scroller with sort 2
by Christopher O
HTML
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/lodash/4.16.6/lodash.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.core.min.js"></script>
<script src="https://cdn.rawgit.com/Marak/faker.js/master/examples/browser/js/faker.js"></script>
<!-- https://github.com/marak/Faker.js/ -->
<div id="app">
<!-- <div id="lefty"> -->
<virtual-scroller :items="items" :renderers="renderers" item-height="22"></virtual-scroller>
<!-- </div> -->
</div>
CSS
body {
font-family: sans-serif;
/* position: relative; */
}
#lefty {
/* position: relative;
width: 200px;
height: 300px;
overflow-y: scroll;
overflow-x: none;
border: 1px solid red;
font-size: 10px;
color: black; */
}
.virtual-scroller {
/* overflow: auto; */
/* position: relative; */
overflow-y: scroll;
overflow-x: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 90%;
width: 250px;
border: 1px solid blue;
}
.item-container {
box-sizing: border-box;
}
.item {
height: 40px;
/* 22 */
padding: 10px;
box-sizing: border-box;
cursor: pointer;
user-select: none;
-moz-user-select: none;
-webkit-user-select: none;
/* border: 1px solid green; */
white-space: nowrap;
}
.letter {
text-transform: uppercase;
color: grey;
font-weight: bold;
}
.item:nth-of-type(odd) {
background: #bbb;
}
JavaScript
console.clear();
useHeight = 40;
Vue.component('virtual-scroller', {
props: ['items', 'renderers', 'itemHeight'],
template: `<div class="virtual-scroller" @scroll="updateVisibleItems">
<div class="item-container" ref="container">
<div class="items">
<component class="item" v-for="item in visibleItems" :is="renderers[item.type]" :item="item"></component>
</div>
</div>
</div>`,
data: () => ({
visibleItems: [],
containerStyle: null,
}),
watch: {
items(val) {
this.l = this.items.length;
this.updateVisibleItems()
}
},
methods: {
updateVisibleItems() {
const el = this.$el;
const scrollTop = el.scrollTop;
const scrollBottom = scrollTop + el.clientHeight;
// console.log('*scrollTop: ' + scrollTop);
// console.log('*el.clientHeight: ' + el.clientHeight);
// console.log('*this.itemHeight: ' + this.itemHeight + "\n----------------------\n");
// let startIndex = Math.floor(scrollTop / this.itemHeight) - 3;
let startIndex = Math.floor(scrollTop / useHeight) - 3;
if (startIndex < 0) {
startIndex = 0;
}
// let endIndex = Math.ceil(scrollBottom / this.itemHeight) + 3;
let endIndex = Math.ceil(scrollBottom / useHeight) + 3;
if (endIndex >= this.l) {
endIndex = this.l;
}
const container = this.$refs.container;
// container.style.paddingTop = startIndex * this.itemHeight + 'px';
container.style.paddingTop = startIndex * useHeight + 'px';
// container.style.height = this.l * this.itemHeight + 'px';
container.style.height = this.l * useHeight + 'px';
this.visibleItems = this.items.slice(startIndex, endIndex);
this.$forceUpdate();
},
},
created() {
this.l = this.items.length;
},
mounted() {
this.updateVisibleItems();
},
}); // END Vue.component('virtual-scroller')
const renderers = {
letter: {
props: ['item'],
template: `<div...