DOM节点创建与性能

DOM nodes creation vs perfomance

本文关键字:性能 创建 节点 DOM      更新时间:2023-09-26

我有一个动态创建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);
}

您可能需要考虑使用某种可回收清单

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

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

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

我敢说,没有其他的方法可以做到这一点-天真地创建150,000个DOM节点将完全摧毁您的滚动性能

一些例子
  • 一个纯JS的例子
  • 一个建立在Polymer
  • 之上的例子另一个在React 之上构建的例子
以下是纯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>

如果还没有,您可能希望隐藏正在其中创建节点的元素,并仅在循环结束时显示它。

。当你在它上面工作时,设置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);

为单行创建一个模板,然后重复克隆它,这可能有助于获得浏览器的帮助:

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));
}

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