JavaScript原生实现简单虚拟列表(列表不定高)

本文首发在我的个人博客上:JavaScript原生实现简单虚拟列表(列表不定高)https://www.brandhuang.com/article/1745637125513

前言

之前实现了一个定高版本的虚拟列表,今天在定高版本的基础上稍作调整,来实现不定高版本,之前的版本请跳转对应文章查看:JavaScript原生实现简单虚拟列表(固定高度) https://www.brandhuang.com/article/1616725433946

先说结论

实现不定高的原理就是:每次把内容渲染到页面后,都去重新获取一次 item 的实际高度,然后再执行一次渲染

最开始想用 createDocumentFragment 创建一个虚拟的文档节点,先将内容渲染到这个虚拟的文档节点中,最后从虚拟文档节点中获取 item 的实际高度,最后,一次插入到真实DOM中。结果发现,在虚拟的文档节点中时拿不到 item 的实际高度的,所以才有了下面的实现方式。

完整代码

不定的高版本代码如下:

html 和 css 与定高版本相比,未做任何调整

    <div class="container"><div class="zhanwei"></div></div><style> .container {border: 1px solid #eee;height: 300px;width: 300px;overflow: auto;position: relative;box-sizing: border-box;}.zhanwei {position: relative;}.item {position: absolute;top: 0;min-height: 50px;width: 100%;border: 1px solid #eee;will-change: transform; box-sizing: border-box;}.item:nth-of-type(odd) {background: #00ccff;}.item:nth-of-type(even) {background: #ffcc00;}</style>

js 代码如下

和定高版本相比,就两处改动,请查看代码中的 变化一 和 变化二

// 不固定高度版本let container = document.querySelector('.container');let zhanwei = document.querySelector('.zhanwei');let itemList = []; // 假设有10000条数据for (let i = 0; i < 10000; i++) {// 生成10000条数据itemList.push({index: i,content: `Item ${i} - ${"Hello world!".repeat(Math.floor(Math.random() * 10))}`});};let buffer = 5; // 多渲染几条,避免滚动看着异常let itemHeight = 50; // 每条数据的一个默认最小高度let heights = new Map();// 记录渲染的每个 item 的高度,为不定高版本做准备let offsets = new Map(); // 记录每个 item 的偏移量,即每个item距离顶部的距离let rendered = new Map(); // 存储已渲染的数据// 更新偏移量, 根据item高度,计算 zhanwei 元素的高度,好让container出现滚动条function updateOffsets() {let offset = 0for (let i = 0; i < itemList.length; i++) {let h = heights.get(i) ?? itemHeight; // ?? 是空值合并运算符,当左边为null或者undefined时使用右边值,和三元运算符相比,排除了 0 的干扰offsets[i] = offset;offset += h + 5; // 加上了5个像素的间距}zhanwei.style.height = offset + 'px';}// 变化一:创建一个重新渲染函数function rerender(item, i) {let height = item.getBoundingClientRect().heightif (heights.get(itemList[i].index) !== height) {heights.set(itemList[i].index, height)updateOffsets()render()}}// 渲染数据function render() {let scrollTop = container.scrollTop;let viewHeight = container.clientHeight;let start = 0; // 查找视口第一个item的索引while (start < itemList.length && offsets[start + 1] < scrollTop) {start++;}let end = start ;// 查找视口最后一个item的索引while (end < itemList.length && offsets[end] < scrollTop + viewHeight) {end++;}start = Math.max(0, start - buffer);end = Math.min(itemList.length, end + buffer);let nextRendered = new Map(); // 当前需要渲染的数据for (let i = start; i < end; i++) {if (!rendered.has(i)) {let item = document.createElement('div')item.className = 'item'item.style.transform = `translateY(${offsets[itemList[i].index] + 'px'})`item.textContent = itemList[i].contentcontainer.appendChild(item)rendered.set(i, item)// 变化二:向页面插入数据后执行一次重新渲染rerender(item, i)}nextRendered.set(i, rendered.get(i))}// 不可见的区域 移除for (const [i, el] of rendered.entries()) {if (!nextRendered.has(i)) {container.removeChild(el);}}// 更新 renderedrendered.clear();for (const [i, el] of nextRendered.entries()) {rendered.set(i, el);}}container.addEventListener("scroll", render);updateOffsets()render()

如果你有更好的实现方案,欢迎留言、贴代码交流。

感谢你的阅读 ❤️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/77280.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

redis数据类型-位域bitfield

redis数据类型-位域bitfield 文档 redis单机安装redis常用的五种数据类型redis数据类型-位图bitmapredis数据类型-基数统计HyperLogLogredis数据类型-地理空间GEOredis数据类型-流Stream 官方文档 官网操作命令指南页面&#xff1a;https://redis.io/docs/latest/commands/…

pandas读取MySQL中的数据

使用pandas读取MySQL中的数据 1、导入库 pip install pandas pip install sqlalchemy2、示例代码 # -*- coding: utf-8 -*-import pandas as pd import re from sqlalchemy import create_engine# 清洗文本 def clean_text(text):text

MyBatis缓存配置的完整示例,包含一级缓存、二级缓存、自定义缓存策略等核心场景,并附详细注释和总结表格

以下是MyBatis缓存配置的完整示例&#xff0c;包含一级缓存、二级缓存、自定义缓存策略等核心场景&#xff0c;并附详细注释和总结表格&#xff1a; 1. 一级缓存&#xff08;默认开启&#xff09; // 使用同一SqlSession执行两次查询&#xff0c;自动命中一级缓存 try (SqlSe…

深入解析 C++17 中的std::variant与std::visit:从原理到实践

引言 什么是std::variant 在 C17 之前&#xff0c;如果你想在一个变量中存储多种可能的类型&#xff0c;通常会使用 union 或 void* 指针。然而&#xff0c;这些方法都有明显的缺点。 使用 union 时&#xff0c;类型信息会丢失&#xff0c;使得代码容易出错。 void* 指针则需…

Dijkstra算法对比图神经网络(GNN)

什么是AI模型? AI模型(人工智能模型)是一类模仿人类智能行为的数学模型或算法。它们通过从大量数据中学习,识别模式、做出预测或决策。常见的AI模型包括机器学习模型(如决策树、神经网络、支持向量机)和深度学习模型(如卷积神经网络CNN、循环神经网络RNN)。简单来说,…

Yarn 安装与使用教程

Yarn 安装与使用教程 Yarn 是一个由 Facebook 开发的 JavaScript 包管理工具&#xff0c;它比传统的 npm 更加高效、可靠&#xff0c;并且在性能上有所提升。Yarn 主要解决了 npm 安装速度慢、并发性差、缓存机制不完善等问题&#xff0c;它提供了更快的安装速度、更稳定的依赖…

Spring Boot 的配置加载顺序

Spring Boot 的配置加载顺序是“后来居上”——优先级高的配置源会覆盖优先级低的配置源中的同名配置 覆盖规则如下&#xff1a; 后加载的配置具有更高的优先级&#xff0c;会覆盖先加载的配置。如果多个配置源中存在同名配置项&#xff0c;最终生效的是具有最高优先级的那个…

Git分支重命名与推送参数解析

这两个参数的解释如下&#xff1a; git branch -M master 中的 -M 参数 -M 是 --move --force 的组合简写&#xff0c;表示强制重命名当前分支为 master。如果当前分支已经存在名为 master 的分支&#xff0c;-M 会强制覆盖它&#xff08;慎用&#xff0c;可能导致数据丢失&…

qt源码编译

问题1&#xff1a; 源码头文件问题&#xff1a; 有部分头文件缺少#include<limits>头文件 home/jetson/qt-everywhere-src-5.15.2/qtbase/include/QtCore/qfloat16.h /home/jetson/qt-everywhere-src-5.15.2/qtbase/src/corelib/text/qbytearraymatcher.h 问题2&…

芯岭技术XL32F003单片机 32位Cortex M0+ MCU简单介绍 性能优异

XL32F003单片机是深圳市芯岭技术有限公司的一款基于 32 位 ARM Cortex-M0 内核的高性能微控制器&#xff0c;提供SOP8/SOP14/SOP16/TSSOP20/SSOP24/QFN20/QFN32多种封装可选&#xff0c;可满足不同设计需求。XL32F003可用于工业控制、手持设备、PC 外设、传感器节点等应用场景&…

计算机图形学实践:结合Qt和OpenGL实现绘制彩色三角形

在Qt项目中结合OpenGL与CMake需要配置正确的依赖关系、链接库以及代码结构设计。以下是具体实现步骤和关键要点&#xff1a; 一、环境准备 安装Qt 确保安装包含OpenGL模块的Qt版本&#xff08;如Qt OpenGL、Qt OpenGLWidgets组件&#xff09;。安装CMake 使用3.10及以上版本&a…

3:QT联合HALCON编程—海康相机SDK二次程序开发

思路&#xff1a; 1.定义带UI界面的主函数类 1.1在主函数中包含其它所有类头文件&#xff0c;进行声明和实例化&#xff1b;使用相机时&#xff0c;是用公共相机的接口在某一个具体函数中去实例化具体的海康相机对象。 1.2设计界面&#xff1a;连接相机&#xff0c;单次采集&a…

基于大模型底座重构司法信息系统

前置篇章&#xff1a;法律智能体所需的基础知识 构建一个高效的法律智能体&#xff0c;特别是在基于RAG&#xff08;Retrieval-Augmented Generation&#xff09;架构的背景下&#xff0c;需要融合多种学科和领域的知识。以下是对法律智能体开发和应用所需核心基础知识的简要介…

类《双人成行》3D动作益智冒险类双人控制游戏开发

服务器端采用了基于开源Kbengine&#xff08;引擎使用C和Python编写&#xff09;的多人在线游戏服务器&#xff0c;客户端采用Unity3D。游戏支持线上的双人联机房间功能。 资源地址&#xff1a;类《双人成行》3D动作益智冒险类双人控制游戏开发教程 | Unity 中文课堂 一、游戏…

Spark--基本介绍

Spark是基于内存的快速&#xff0c;通农用&#xff0c;可拓展的大数据分析计算引擎&#xff0c;Hadoop是一个分布式系统基础架构 Spark和Hadoop之间的对比和联系 架构与组件&#xff1a; Hadoop&#xff1a; ■ HDFS&#xff1a;分布式文件系统&#xff0c;负责海量数据存储。…

05-GPIO原理

一、概述 1、GPIO,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、控制外部硬件或者采集外部硬件数据的功能。 2、GPIO的复用:引脚复用是指将单个引脚配置为多个功能的能力。在 STM32 中&…

基于LangChain4J的AI Services实践:用声明式接口重构LLM应用开发

基于LangChain4J的AI Services实践&#xff1a;用声明式接口重构LLM应用开发 前言&#xff1a;当Java开发遇上LLM编程困境 在LLM应用开发领域&#xff0c;Java开发者常面临两大痛点&#xff1a;一是需要手动编排Prompt工程、记忆管理和结果解析等底层组件&#xff0c;二是复杂…

深入解析 Docker 容器进程的 cgroup 和命名空间信息

深入解析 Docker 容器进程的 cgroup 和命名空间信息 在现代 Linux 系统中&#xff0c;控制组&#xff08;cgroup&#xff09;和命名空间&#xff08;namespace&#xff09;是实现容器化技术的核心机制。cgroup 用于管理和限制进程的资源使用&#xff08;如 CPU、内存、I/O&…

【汽车ECU电控数据管理篇】S19文件格式解析篇章

一、S19格式是啥 在电控文件管理的初期阶段&#xff0c;我首次接触到的是 A2L 和 HEX 文件。其中&#xff0c;A2L 文件主要承担着描述性功能&#xff0c;它详细地描述了各种参数和配置等相关信息。而 HEX 文件则是一种刷写文件&#xff0c;其内部明确记录了具体的地址以及对应的…

python编程相关的单词

the: 在编程中&#xff0c;“the” 是一个常见的英语单词&#xff0c;用于指定特定的对象或变量。例如&#xff0c;“the function” 指的是某个特定的函数。 the的拼写是t,h,e.再读一次t,h,e and: 在编程中&#xff0c;“and” 是一个逻辑运算符&#xff0c;用于连接两个条件&…