【问题标题】:Large list rendering in JavaScriptJavaScript 中的大列表渲染
【发布时间】:2013-07-13 03:32:51
【问题描述】:

我正在尝试基于虚拟渲染概念来渲染列表。我面临一些小问题,但它们并没有阻止这种行为。这是工作小提琴http://jsfiddle.net/53N36/9/,这是我的问题

  1. 最后一项不可见,我假设有些地方我错过了索引。(已修复,请参阅编辑)
  2. 如果我想为此添加自定义滚动,如何计算 scrollPosition。
  3. 这是最好的方法还是其他方法?

我已经用 700000 个项目和 70 个 chrome 项目对其进行了测试。下面是代码

(function () {
var list = (function () {
    var temp = [];
    for (var i = 0, l = 70; i < l; i++) {
        temp.push("list-item-" + (i + 1));
    }
    return temp;
}());

function listItem(text, id) {
    var _div = document.createElement('div');
    _div.innerHTML = text;
    _div.className = "listItem";
    _div.id = id;
    return _div;
}
var listHold = document.getElementById('listHolder'),
    ht = listHold.clientHeight,
    wt = listHold.clientWidth,
    ele = listItem(list[0], 'item0'),
    frag = document.createDocumentFragment();
listHold.appendChild(ele);
var ht_ele = ele.clientHeight,
    filled = ht_ele,
    filledIn = [0];
for (var i = 1, l = list.length; i < l; i++) {
    if (filled + ht_ele < ht) {
        filled += ht_ele;
        ele = listItem(list[i], 'item' + i);
        frag.appendChild(ele);
    } else {
        filledIn.push(i);
        break;
    }
}
listHold.appendChild(frag.cloneNode(true));
var elements = document.querySelectorAll('#listHolder .listItem');

function MouseWheelHandler(e) {
    var e = window.event || e;
    var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
    console.log(delta);
    //if(filledIn[0] != 0 && filledIn[0] != list.length){
    if (delta == -1) {
        var start = filledIn[0] + 1,
            end = filledIn[1] + 1,
            counter = 0;
        if (list[start] && list[end]) {
            for (var i = filledIn[0]; i < filledIn[1]; i++) {
                if (list[i]) {
                    (function (a) {
                        elements[counter].innerHTML = list[a];
                    }(i));
                    counter++;
                }
            }
            filledIn[0] = start;
            filledIn[1] = end;
        }
    } else {
        var start = filledIn[0] - 1,
            end = filledIn[1] - 1,
            counter = 0;
        if (list[start] && list[end]) {
            for (var i = start; i < end; i++) {
                if (list[i]) {
                    (function (a) {
                        elements[counter].innerHTML = list[a];
                    }(i));
                    counter++;
                }
            }
            filledIn[0] = start;
            filledIn[1] = end;
        }
    }
    //}
}
if (listHold.addEventListener) {
    listHold.addEventListener("mousewheel", MouseWheelHandler, false);
    listHold.addEventListener("DOMMouseScroll", MouseWheelHandler, false);
} else listHold.attachEvent("onmousewheel", MouseWheelHandler);
}());

请在这方面给我建议。

编辑: 我又试了一次,我能够解决索引问题。 http://jsfiddle.net/53N36/26/ 但是如何根据当前显示的数组列表计算滚动位置。

【问题讨论】:

    标签: javascript algorithm progressive


    【解决方案1】:

    Is this the best method or any other?
    我认为让这更容易的事情是不要尝试自己处理滚动。
    this fiddle 中,我展示了您可以让浏览器为您处理滚动,即使我们使用的是 virtual rendering

    使用.scrollTop,我检测浏览器认为用户正在查看的位置,并据此绘制项目。
    您会注意到,如果您将 hidescrollbar 设置为 false 并且用户使用它来滚动,我的方法仍然可以正常运行。

    因此,对于calculate scroll position,您可以使用.scrollTop
    至于自定义滚动,请确保您影响#listHolder.scrollTop 并调用refreshWindow()

    来自 FIDDLE 的代码

    (function () {
        //CHANGE THESE IF YOU WANT
        var hidescrollbar = false;
        var numberofitems = 700000;
        //
    
        var holder = document.getElementById('listHolder');
        var view = null;
    
        //get the height of a single item
        var itemHeight = (function() {
            //generate a fake item
            var div = document.createElement('div');
            div.className = 'listItem';
            div.innerHTML = 'testing height';
            holder.appendChild(div);
    
            //get its height and remove it
            var output = div.offsetHeight;
            holder.removeChild(div);
            return output;
        })();
    
        //faster to instantiate empty-celled array
        var items = Array(numberofitems);
        //fill it in with data
        for (var index = 0; index < items.length; ++index)
            items[index] = 'item-' + index;
    
        //displays a suitable number of items
        function refreshWindow() {
            //remove old view
            if (view != null)
                holder.removeChild(view);
            //create new view
            view = holder.appendChild(document.createElement('div'));
    
            var firstItem = Math.floor(holder.scrollTop / itemHeight);
            var lastItem = firstItem + Math.ceil(holder.offsetHeight / itemHeight) + 1;
            if (lastItem + 1 >= items.length)
                lastItem = items.length - 1;
    
            //position view in users face
            view.id = 'view';
            view.style.top = (firstItem * itemHeight) + 'px';
    
            var div;
            //add the items
            for (var index = firstItem; index <= lastItem; ++index) {
                div = document.createElement('div');
                div.innerHTML = items[index];
                div.className = "listItem";
                view.appendChild(div);
            }
            console.log('viewing items ' + firstItem + ' to ' + lastItem);
        }
    
        refreshWindow();
    
        document.getElementById('heightForcer').style.height = (items.length * itemHeight) + 'px';
        if (hidescrollbar) {
            //work around for non-chrome browsers, hides the scrollbar
            holder.style.width = (holder.offsetWidth * 2 - view.offsetWidth) + 'px';
        }
    
        function delayingHandler() {
            //wait for the scroll to finish
            setTimeout(refreshWindow, 10);
        }
        if (holder.addEventListener)
            holder.addEventListener("scroll", delayingHandler, false);
        else
            holder.attachEvent("onscroll", delayingHandler);
    }());
    
    <div id="listHolder">
        <div id="heightForcer"></div>
    </div>
    
    html, body {
        width:100%;
        height:100%;
        padding:0;
        margin:0
    }
    body{
        overflow:hidden;
    }
    .listItem {
        border:1px solid gray;
        padding:0 5px;
        width: margin : 1px 0px;
    }
    #listHolder {
        position:relative;
        height:100%;
        width:100%;
        background-color:#CCC;
        box-sizing:border-box;
        overflow:auto;
    }
    /*chrome only
    #listHolder::-webkit-scrollbar{
        display:none;
    }*/
    #view{
        position:absolute;
        width:100%;
    }
    

    【讨论】:

    • 请你看看这个问题stackoverflow.com/questions/18285760/…。我使用相同的代码,但用于水平列表。你能回答吗
    • 这很整洁。我担心的是它需要元素的高度一致......但也许这正是虚拟渲染必须工作的方式。
    • 你可以有不同的高度,但是根据你计算元素高度的方式,评估一组元素的总高度可能会非常慢。这是一个常数的简单乘法,但如果它们是变量,则必须迭代求和
    • 虽然我很欣赏在我的 2 小时 hack 中传播了一个专用且受支持的库,但请比“或只是使用”更有礼貌,并承认这个库没有当问题被回答时,根本就存在。也许不那么轻蔑的“现在有一个很酷的库” 会更符合 SO 的价值观。此外,为了减轻对广告的任何担忧,最好披露您是创作者clusterize@Denis
    • 哇,我不是故意冒犯你@Hashbrown。对此感到抱歉。没错,我就是这个插件的创造者
    猜你喜欢
    • 2022-08-16
    • 1970-01-01
    • 2019-09-14
    • 1970-01-01
    • 2021-06-22
    • 2019-11-07
    • 1970-01-01
    • 2012-03-13
    • 1970-01-01
    相关资源
    最近更新 更多