DOM节点创建与性能

时间:2016-09-30 01:30:55

标签: javascript

我有一个动态创建DOM节点的功能,以容纳来自服务器的GPS JSON数据。随着节点数量的增加,浏览器性能也会提高 降低。有没有办法重写/调整功能以提高性能?函数的简化节点创建部分的工作原理如下所示:

var table = document.createElement('table');
var tbody = document.createElement('tbody');
table.appendChild(tbody);
for(var i = 0; i<3000;i++){
    var tr = document.createElement('tr');
    for(var j=0;j<50;j++){
        var td = document.createElement('td');
        td.style.backgroundColor="#a"+j%10+'c';
        tr.appendChild(td);
    }
    tbody.appendChild(tr);
}

3 个答案:

答案 0 :(得分:1)

您可能需要考虑使用某种可回收列表

此类列表通过删除位于视口外部的DOM节点并在当前查看时重新创建它们来回收其元素。

长话短说 - 在任何给定时间,DOM中只存在当前查看的DOM节点

另一种技术是使用固定数量的单元格,然后根据用户滚动的位置填充数据

AFAIK没有其他方法可以解决这个问题 - 天真地创建150,000个DOM节点会完全毁掉你的滚动性能

一些例子

以下是纯JS示例处理此问题的方法:

来源:http://elliottsprehn.com/personal/infinite-scroll.html

<!DOCTYPE html>
<style>
    * { box-sizing: border-box; }
    ul, li { list-style-type: none; padding: 0; margin: 0; }

    h1 { font-size: 1.2em; }

    #infinite-list {
        width: 300px;
        border: 1px solid #ccc;
        margin: 1em;
        position: relative;
    }

    #infinite-list .scroll-view {
        overflow-y: scroll;
        height: -webkit-calc(20em + 2px);
    }

    #infinite-list ul {
        position: absolute;
        top: 0;
        width: -webkit-calc(100% - 16px);
    }

    #infinite-list li { 
        height: 2em;
        line-height: 2em;
        padding-left: 1em;
        border-bottom: 1px solid #ccc;
    }

    #infinite-list li:last-child { 
        border-bottom: none;
    }

    #infinite-list .scroll-view .spacer {
        height: -webkit-calc(2em * 20000);
    }
</style>

<h1>Infinite Scrolling with a fixed number of DOM nodes</h1>

<div id="infinite-list">
    <div class="scroll-view">
        <ul>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
            <li><a href=""></a></li>
        </ul>
        <div class="spacer"></div>
    </div>
</div>

<script>
(function() {
    function randRange(low, high) {
         return Math.floor(Math.random() * (high - low)) + low;
    }

    function randPick(array) {
        return array[randRange(0, array.length)];
    }

    // Randomly create a bunch of test data.
    var tlds = ['.com', '.net', '.org', '.edu', '.co.uk'];
    var domains = ['google', 'facebook', 'yahoo', 'apple', 'youtube', 'amazon'];
    var data = [];
    for (var i = 0; i < 20000; ++i) {
        data.push(i + ' ' + randPick(domains) + randPick(tlds));
    }

    var list = document.querySelector('#infinite-list');
    var listItems = document.querySelectorAll('#infinite-list li');
    var scrollView = document.querySelector('#infinite-list .scroll-view');

    var itemHeight = listItems[0].getBoundingClientRect().height;
    var previous;

    // Propagate scrolling with the mouse wheel.
    list.onmousewheel = function(e) {
        var delta = e.wheelDeltaY;
        if (Math.abs(delta) < itemHeight) {
            delta = itemHeight * (delta > 0 ? 1 : -1);
        }
        scrollView.scrollTop -= delta;
    };

    function update() {
        var current = scrollView.scrollTop;
        if (previous == current) {
            webkitRequestAnimationFrame(update);
            return;
        }
        previous = current;
        var first = Math.ceil(current / itemHeight);
        for (var i = 0; i < listItems.length; ++i) {
            listItems[i].firstElementChild.textContent = data[first++];
        }
        webkitRequestAnimationFrame(update);
    }
    update();

})();
</script>

答案 1 :(得分:1)

如果您还没有,可能需要隐藏正在创建节点的元素,并且只在循环结束时显示它。

E.g。在您处理它时,在其上设置display:none;,然后切换回display:block;(或其中任何一个)。

更新:实际上,你甚至不需要隐藏它,尽管这是一种方法。更简洁的方法是等待将表添加到文档中,直到完成向其添加所有节点,然后仅将其添加到正文中。

var table = document.createElement('table');
var tbody = document.createElement('tbody');
for(var i = 0; i<3000;i++){
    var tr = document.createElement('tr');
    for(var j=0;j<50;j++){
        var td = document.createElement('td');
        td.innerHTML = "#a"+j%10+'c';
        td.style.backgroundColor="#a"+j%10+'c';
        tr.appendChild(td);
    }
    tbody.appendChild(tr);
}
table.appendChild(tbody);
document.body.appendChild(table);

答案 2 :(得分:0)

通过为单行创建模板,然后重复克隆它可能有助于登记浏览器的帮助:

var table = document.createElement('table');
var tbody = document.createElement('tbody');
table.appendChild(tbody);
// Make a template row once up front
var rowtemplate = document.createElement('tr');
for(var j=0;j<50;j++){
    var td = document.createElement('td');
    td.style.backgroundColor="#a"+j%10+'c';
    tr.appendChild(td);
}
// Now clone it over and over instead of creating it piecemeal repeatedly
// Must pass true to ensure it clones whole tree, not just top level tr
for(var i = 0; i<3000;i++){
    table.appendChild(rowtemplate.cloneNode(true));
}

显然,这取决于浏览器内部是否有帮助,但通常情况下,浏览器应该能够比从头开始手动创建每个元素更快地克隆现有节点树。

相关问题