js实现前端无限滚动中的虚拟列表效果(只渲染可视区域,dom元素可复用)

21 篇文章 1 订阅

先看效果

不加防抖节流。
在这里插入图片描述
加防抖节流
在这里插入图片描述

提示

加防抖节流 可以节省性能,但滑动过快会出现闪烁现象。请根据需求自行调整触发时间。原理通过代码可看出!

限制:该代码处理方式由于html 元素高度存在最大值,且不同浏览器以及不同浏览器版本支持的html 元素高度最大值不一样,当数据列表理论生成的高度大于html元素高度最大值时,生成的滚动区域只有html元素高度最大值那部分,剩下部分无法显示出来。

代码

不加防抖节流
<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta name="format-detection" content="telephone=no, email=no">
</head>

<body>
    <header>
        <!-- <h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1> -->
        <h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1>
    </header>
    </article>
    <article class="d-part d-effect">
        <style>
            .container {
                height: 600px;
                overflow: auto;
            }

            .item {
                min-height: 60px;
                border-bottom: 1px solid #cccccc;
                border-top: 1px solid #cccccc;
                width: 100%;
                text-align: center;
                background-color: darkgray;
                /* padding: 30px 0;
                box-sizing: border-box; */
            }
        </style>
        <div class="container">
            <div class="content">
                <div class="viewArea">
                    <div class="item">0</div>
                    <div class="item">1</div>
                    <div class="item">2</div>
                    <div class="item">3</div>
                    <div class="item">4</div>
                    <div class="item">5</div>
                    <div class="item">6</div>
                    <div class="item">7</div>
                    <div class="item">8</div>
                    <div class="item">9</div>
                </div>

            </div>
        </div>
        <script>
            var item = document.querySelector('.viewArea .item');  //需要渲染的单个列表元素
            var container = document.querySelector('.container');  //可视区域元素

            console.log(item);
            var start = 0; // 开始位置
            var pageSize = 10; // 每页展示的数据
            var total = 100000; //数据总长度

            // var itemHeight = 61; // 每个item的高度
            var itemStyle = getComputedStyle(item); // 获取元素最终样式
            var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
            console.log('itemHeight', itemHeight);


            // 设置数据列表的总高度
            document.querySelector('.container .content').style.height = itemHeight * total + 'px';
            updateDom(start, pageSize, itemHeight, 0);
            
            //更新渲染列表函数
            function updateDom(start, pageSize, itemHeight, height) {
                document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
                let all = document.querySelectorAll(' .viewArea .item'); // 获取所有渲染列表
                for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
                    var index = i % pageSize; // 计算出数据列表的下标
                    all[itemIndex].innerHTML = i;
                }
            }
            // 滚动处理函数
            function handleScroller() {
                var lastStart = 0; // 上次开始的位置
                return () => {
                    var currentScrollTop = container.scrollTop;
                    var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight; 
                    var start = Math.floor(currentScrollTop / itemHeight);

                    if (lastStart !== start) {
                        lastStart = start;
                        updateDom(start, pageSize, itemHeight, fixedScrollTop);
                    }
                }
            }
            // // 防抖和节流
            // function throttle(fn, delay, atleast) {
            //     let timer = null;
            //     let rAFtimer = null;
            //     let previous = 0;

            //     return function () {
            //         let now = Date.now();
            //         if (now - previous > atleast) {
            //             console.log('now - previous > atleast');
            //             fn();
            //             previous = now;
            //         } else {
            //             if (delay > 20) {
            //                 console.log('delay > 20');
            //                 clearTimeout(timer);
            //                 timer = setTimeout(function () {
            //                     fn();
            //                     previous = 0;
            //                 }, delay);
            //             } else {
            //                 console.log('delay < 20');
            //                 cancelAnimationFrame(rAFtimer);
            //                 rAFtimer = requestAnimationFrame(function () {
            //                     requestIdleCallback(fn);
            //                 });
            //             }
            //         }
            //     }
            // }
            // document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
            document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
        </script>
    </article>
    </div>
</body>

</html>
加防抖节流
<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</title>
    <meta name="viewport" content="initial-scale=1.0">
    <meta name="format-detection" content="telephone=no, email=no">
</head>

<body>
    <header>
        <h1>无限滚动中的虚拟列表-防抖和节流(只渲染可视区域,dom元素可复用)</h1>
        <!-- <h1>无限滚动中的虚拟列表(只渲染可视区域,dom元素可复用)</h1> -->
    </header>
    </article>
    <article class="d-part d-effect">
        <style>
            .container {
                height: 600px;
                overflow: auto;
            }

            .item {
                min-height: 60px;
                border-bottom: 1px solid #cccccc;
                border-top: 1px solid #cccccc;
                width: 100%;
                text-align: center;
                background-color: darkgray;
                /* padding: 30px 0;
                box-sizing: border-box; */
            }
        </style>
        <div class="container">
            <div class="content">
                <div class="viewArea">
                    <div class="item">0</div>
                    <div class="item">1</div>
                    <div class="item">2</div>
                    <div class="item">3</div>
                    <div class="item">4</div>
                    <div class="item">5</div>
                    <div class="item">6</div>
                    <div class="item">7</div>
                    <div class="item">8</div>
                    <div class="item">9</div>
                </div>

            </div>
        </div>
        <script>
            var item = document.querySelector('.viewArea .item');
            var container = document.querySelector('.container');

            console.log(item);
            var start = 0; // 开始位置
            var pageSize = 10; // 每页展示的数据
            var total = 100000; //数据总长度

            // var itemHeight = 61; // 每个item的高度
            var itemStyle = getComputedStyle(item);
            var itemHeight = Number(itemStyle.height.split('px')[0]) + Number(itemStyle.borderTopWidth.split('px')[0]) + Number(itemStyle.borderBottomWidth.split('px')[0]); // 每个item的高度
            console.log('itemHeight', itemHeight);


            // 设置数据列表的总高度
            document.querySelector('.container .content').style.height = itemHeight * total + 'px';
            updateDom(start, pageSize, itemHeight, 0);
            function updateDom(start, pageSize, itemHeight, height) {
                document.querySelector('.container .content .viewArea').style.transform = 'translateY(' + height + 'px)';
                let all = document.querySelectorAll(' .viewArea .item');
                for (var i = start, itemIndex = 0, len = start + pageSize; i < len; i++, itemIndex++) {
                    var index = i % pageSize;
                    all[itemIndex].innerHTML = i;
                }
            }
            // 滚动处理函数
            function handleScroller() {
                var lastStart = 0; // 上次开始的位置
                return () => {
                    var currentScrollTop = container.scrollTop;
                    var fixedScrollTop = currentScrollTop - currentScrollTop % itemHeight;
                    var start = Math.floor(currentScrollTop / itemHeight);

                    if (lastStart !== start) {
                        lastStart = start;
                        updateDom(start, pageSize, itemHeight, fixedScrollTop);
                    }
                }
            }
            // 防抖和节流
            function throttle(fn, delay, atleast) {
                let timer = null;
                let rAFtimer = null;
                let previous = 0;

                return function () {
                    let now = Date.now();
                    if (now - previous > atleast) {
                        console.log('now - previous > atleast');
                        fn();
                        previous = now;
                    } else {
                        if (delay > 20) {
                            console.log('delay > 20');
                            clearTimeout(timer);
                            timer = setTimeout(function () {
                                fn();
                                previous = 0;
                            }, delay);
                        } else {
                            console.log('delay < 20');
                            cancelAnimationFrame(rAFtimer);
                            rAFtimer = requestAnimationFrame(function () {
                                requestIdleCallback(fn);
                            });
                        }
                    }
                }
            }
            document.querySelector('.container').addEventListener('scroll', throttle(handleScroller(), 16, 500), false);
            // document.querySelector('.container').addEventListener('scroll', handleScroller(), false);
        </script>
    </article>
    </div>
</body>

</html>
  • 7
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值