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.pswp.cn/web/77280.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/77280.shtml
英文地址,請注明出處:http://en.pswp.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;用于連接兩個條件&…