JavaScript中的大型列表呈现

时间:2013-07-13 03:32:52

标签: javascript algorithm progressive

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

  1. 最后的项目不可见,我假设有些地方错过了索引。(固定,请看编辑)
  2. 如果我想为此添加自定义滚动,如何计算scrollPosition。
  3. 这是最好的方法还是其他方法?
  4. 我用700000件物品和70件镀铬物品进行了测试。以下是代码

    (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/ 但是如何根据当前显示的数组列表计算滚动位置。

1 个答案:

答案 0 :(得分:9)

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

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

因此,calculate scroll position您可以.scrollTop使用.scrollTop 至于自定义滚动,只需确保影响#listHolder的{​​{1}}并回忆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%;
}