- У меня есть список элементов в Vue. js с индексом от 0 до 49
- Я показываю подмножество этих элементов, контролируемых startIndex и endIndex
- Когда Я увеличиваю startIndex до 1, отображаются элементы с 1-49, 0 удаляется из DOM
- Как получить высоту 0, которая была только что удалена
- Также как получить высоту элемента, который был только что добавил, если я отредактирую endIndex?
HTML
<script type="text/x-template" id="virtual-list">
<div id="root" ref="root">
<div id="viewport" ref="viewport">
<div id="spacer" ref="spacer" :style="spacerStyle">
<div v-for="i in visibleItems" :key="i.index" :id="i.index" class="list-item">
{{i.value}}
</div>
</div>
</div>
</div>
</script>
<div id="app">
<button @click="incrementStart">Start +</button>
<button @click="decrementStart">Start -</button>
<button @click="incrementEnd">End +</button>
<button @click="decrementEnd">End -</button>
<virtual-list></virtual-list>
</div>
CSS
* {
box-sizing: border-box;
}
html,
body,
#app {
height: 100%;
}
#app {
padding: 1.25rem;
}
#root {
height: 50%;
overflow-y: auto;
}
.list-item {
padding: 0.75rem 0;
}
Vue. js
const PAGE_SIZE = 50;
const items = new Array(PAGE_SIZE).fill(null).map((item, index) => {
return {
id: faker.random.uuid(),
index: index,
value: "Item " + index + " " + faker.random.words(index % 25)
};
});
const bus = new Vue({});
Vue.component("virtual-list", {
template: "#virtual-list",
data() {
return {
isMounted: false,
items,
startIndex: 0,
endIndex: PAGE_SIZE,
scrollTop: 0,
translateY: 0,
scrollDirection: 0
};
},
computed: {
visibleItems() {
return this.items.slice(this.startIndex, this.endIndex);
},
/**
Translate the spacer verticaly to keep the scrollbar intact
We only show N items at a time so the scrollbar would get affected if we dont translate
*/
spacerStyle() {
return {
willChange: "auto",
transform: "translateY(" + this.translateY + "px)"
};
}
},
methods: {
handleScroll() {
this.scrollTop = this.$el.scrollTop;
this.startIndex = Math.floor(this.scrollTop / 42);
}
},
watch: {
scrollTop(newValue, oldValue) {
if (newValue > oldValue) {
this.scrollDirection = 1;
} else if (newValue < oldValue) {
this.scrollDirection = -1;
}
},
startIndex(newValue, oldValue) {
// console.log(this.$refs.spacer.children);
}
},
beforeUpdate() {
// console.log('before update', this.$refs.spacer.children);
},
mounted() {
this.isMounted = true;
const children = this.$refs.spacer.children;
for (let i = 0; i < children.length; i++) {
// console.log(children[i].offsetTop - this.$el.offsetTop);
children[i].setAttribute("data-height", children[i].scrollHeight);
}
bus.$on("incrementStart", () => {
this.startIndex++;
});
bus.$on("decrementStart", () => {
this.startIndex--;
});
bus.$on("incrementEnd", () => {
this.endIndex++;
});
bus.$on("decrementEnd", () => {
this.endIndex--;
});
this.$el.addEventListener("scroll", this.handleScroll);
},
destroyed() {
this.$el.removeEventListener("scroll", this.handleScroll);
}
});
new Vue({
el: "#app",
methods: {
incrementStart() {
bus.$emit("incrementStart");
},
decrementStart() {
bus.$emit("decrementStart");
},
incrementEnd() {
bus.$emit("incrementEnd");
},
decrementEnd() {
bus.$emit("decrementEnd");
}
}
});