在javascript性能中动态创建大型html表

时间:2011-02-01 15:16:18

标签: javascript jquery performance

我有一个用于数据分析的应用程序,我在创建表时遇到了一些性能问题。数据是从文档中提取的,重要的是所有数据都显示在一个页面上(不幸的是,分页不是一个选项)。

使用jQuery,我向服务器发出ajax请求以检索数据。完成请求后,我将数据传递给输出函数。输出函数使用for循环遍历数据数组,并将行连接到变量。循环完成后,包含该表的变量将附加到页面上的现有div,然后我继续将事件绑定到表以处理数据。

使用一小组数据(~1000-2000行),它的工作效果相对较好,但有些数据集包含超过10,000行,导致Firefox崩溃,关闭或无响应。

我的问题是,有没有更好的方法来完成我正在做的事情?

以下是一些代码:

//This function gets called by the interface with an id to retrieve a document
function loadDocument(id){
    $.ajax({
        method: "get",
        url: "ajax.php",
        data: {action:'loadDocument',id: id},
        dataType: 'json',
        cache: true,
        beforeSend: function(){
            if($("#loading").dialog('isOpen') != true){
                //Display the loading dialog
                $("#loading").dialog({
                    modal: true
                });
            }//end if
        },//end beforesend
        success: function(result){
            if(result.Error == undefined){
                outputDocument(result, id);
            }else{
                <handle error code>
            }//end if
            if($('#loading').dialog('isOpen') == true){
                //Close the loading dialog
                $("#loading").dialog('close');
            }//end if
        }//end success
    });//end ajax
};//end loadDocument();


//Output document to screen
function outputDocument(data, doc_id){

    //Begin document output
    var rows = '<table>';
    rows += '<thead>';
    rows += '<tr>';
    rows += '<th>ID</th>';
    rows += '<th>Status</th>';
    rows += '<th>Name</th>';
    rows += '<th>Actions</th>';
    rows += '<th>Origin</th>';
    rows += '</tr>';
    rows += '</thead>';
    rows += '<tbody>';

    for(var i in data){
        var recordId = data[i].id;
        rows += '<tr id="' + recordId + '" class="' + data[i].status + '">';
        rows += '<td width="1%" align="center">' + recordId + '</td>';
        rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
        rows += '<td width="70%"><span class="name">' + data[i].name + '</span></td>';
        rows += '<td width="2%">';
        rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
        rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
        rows += '</td>';
        rows += '<td width="1%">' + data[i].origin + '</td>';
        rows += '</tr>';
    }//end for

    rows += '</tbody>';
    rows += '</table>';
    $('#documentRows').html(rows);

我最初使用的是每个循环的jQuery,但切换到for循环,削减了一些ms。

我想过使用谷歌齿轮之类的东西尝试卸载一些处理(如果在这种情况下可能的话)。

有什么想法吗?

7 个答案:

答案 0 :(得分:19)

joinHi,

渲染是一个问题,但是在循环中连接这么多字符串也存在问题,特别是一旦字符串变得非常大。最好将字符串放入数组的各个元素中,然后最终使用“join”一次创建巨大的字符串。 e.g。

var r = new Array();
var j = -1, recordId;
r[++j] =  '<table><thead><tr><th>ID</th><th>Status</th><th>Name</th><th>Actions</th><th>Origin</th></tr></thead><tbody>'; 
for (var i in data){
    var d = data[i];
    recordId = d.id;
    r[++j] = '<tr id="';
    r[++j] = recordId;
    r[++j] = '" class="';
    r[++j] = d.status;
    r[++j] = '"><td width="1%" align="center">';
    r[++j] = recordId;
    r[++j] = '</td><td width="1%" align="center"><span class="status" rel="';
    r[++j] = recordId;
    r[++j] = '"><strong>';
    r[++j] = d.status;
    r[++j] = '</strong></span></td><td width="70%"><span class="name">';
    r[++j] = d.name;
    r[++j] = '</span></td><td width="2%"><input type="button" class="failOne" rev="';
    r[++j] = recordId;
    r[++j] = '" value="F"><input type="button" class="promoteOne" rev="';
    r[++j] = recordId;
    r[++j] = '" value="P"></td><td width="1%">';
    r[++j] = d.origin;
    r[++j] = '</td></tr>';
}
r[++j] = '</tbody></table>';
$('#documentRows').html(r.join(''));

此外,我会使用此处显示的数组索引方法,而不是使用“推送”,因为根据this article,除了谷歌浏览器之外的所有浏览器都更快。

答案 1 :(得分:4)

显示许多行导致浏览器的呈现引擎变慢,而不是JavaScript引擎。不幸的是,你无法做很多事情。

最好的解决方案是不要通过分页或虚拟滚动同时显示这么多行。

答案 2 :(得分:2)

构建字符串的方式将导致大量垃圾回收。

随着字符串越来越长,javascript引擎必须继续分配更大的缓冲区并丢弃旧的缓冲区。最终,如果不回收所有旧字符串的剩余部分,它将无法分配足够的内存。

随着字符串变长,这个问题会变得更糟。

而是尝试使用jQuery manipulation API

一次向DOM添加新元素

另外,请考虑仅渲染可见内容并实现自己的滚动。

答案 3 :(得分:1)

您可以采取以下措施来提高效果:

  1. 你的变量越来越大,所以不要将html存储在一个变量中。解决方案可以是$.each()函数,每个函数都将元素附加到DOM中。但这是一个小调整。
  2. Html生成很好,但您可以尝试DOM创建和追加。像$('<tr></tr>')
  3. 最后,这肯定会解决您的问题:在第一个ajax调用中使用多个ajax调用收集可用的数据量并获取大约1,000个或者可能是更多数据。并使用其他调用来收集剩余数据。如果需要,可以明智地使用同步调用或异步调用。
  4. 但尽量避免存储该值。你的DOM大小将是巨大的,但它应该适用于现代浏览器而忘记IE6。

    @ fuel37:示例

    function outputDocumentNew(data, doc_id) {
        //Variable DOM's
        var rowSample = $('<tr></tr>').addClass('row-class');
        var colSample = $('<td></td>').addClass('col-class');
        var spanSample = $('<span></span>').addClass('span-class');
        var inputButtonSample = $('<input type="button"/>').addClass('input-class');
    
        //DOM Container 
        var container = $('#documentRows');
        container.empty().append('<table></table>');
    
        //Static part
        var head = '<thead>\
                    <tr>\
                        <th width="1%" align="center">ID</th>\
                        <th width="1%" align="center">Status</th>\
                        <th width="70%">Name</th>\
                        <th width="2%">Actions</th>\
                        <th width="1%">Origin</th>\
                    </tr>\
                    </thead>';
        container.append(head);
    
        var body = $('<tbody></tbody>');
        container.append(body);
    
        //Dynamic part
        $.each(data, function (index, value) {
            var _this = this;
    
            //DOM Manupulation
            var row = rowSample.clone();
    
            //Actions
            var inpFailOne = inputButtonSample.clone().val('F').attr('rev', _this.id).addClass('failOne').click(function (e) {
                //do something when click the button.
            });
            var inpPromoteOne = inputButtonSample.clone().val('P').attr('rev', _this.id).addClass('promoteOne').click(function (e) {
                //do something when click the button.
            });
    
            row
            .append(colSample.clone().append(_this.id))
            .append(colSample.clone().append(spanSample.colne().addClass('status').append(_this.status)))
            .append(colSample.clone().append(spanSample.colne().addClass('name').append(_this.name)))
            .append(colSample.clone().append(inpFailOne).append(inpPromoteOne))
            .append(colSample.clone().append(_this.origin));
    
            body.append(row);
        });
    }
    

    在这个过程中你需要创建&amp;维护id或类以进行操作。您可以控制绑定事件并操纵那里的每个元素。

答案 4 :(得分:0)

回答格式化

如果你这样做会怎样?

for(var i in data){
    var record = data[i];
    var recordId = record.id;
    rows += '<tr id="' + recordId + '" class="' + record.status + '">';
    rows += '<td width="1%" align="center">' + recordId + '</td>';
    rows += '<td width="1%" align="center"><span class="status" rel="' + recordId + '"><strong>' + data[i].status + '</strong></span></td>';
    rows += '<td width="70%"><span class="name">' + record.name + '</span></td>';
    rows += '<td width="2%">';
    rows += '<input type="button" class="failOne" rev="' + recordId + '" value="F">';
    rows += '<input type="button" class="promoteOne" rev="' + recordId + '" value="P">';
    rows += '</td>';
    rows += '<td width="1%">' + record.origin + '</td>';
    rows += '</tr>';
}//end for

答案 5 :(得分:0)

根据其他人的建议(我还没有足够的声誉来评论,抱歉!),您可以尝试TableSorter plugin来处理一次只显示可用的数据量。

我不知道它在非常多的行数下是如何运行的,但它们的示例数据大约是1000行。

这对JS性能没有帮助,但会减轻浏览器渲染器的负担。

答案 6 :(得分:0)

可以试试这个......

Improve Loops

Improve String Concat

var tmpLst = [];

for (var i=0, il=data.length; i<il; i++) {
    var record = data[i];
    var recordId = record.id;

    tmpLst.push('<tr id="');
    tmpLst.push(recordId); 
    tmpLst.push('" class="'); 
    tmpLst.push(record.status);
    tmpLst.push('">');
    tmpLst.push('<td width="1%" align="center">');

...ect...


}
rows += tmpLst.join('');

这可能会挤出额外的性能......

var lstReset = i * lstReset.length;
tmpLst[lstReset + 1]='<tr id="';
tmpLst[lstReset + 2]=recordId; 
tmpLst[lstReset + 3]='" class="';