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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4864294/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 14:28:20  来源:igfitidea点击:

Dynamic creation of large html table in javascript performance

javascriptjqueryperformance

提问by fuel37

I have an application which is used for data analysis and I'm having a few performance issues with the creation of the table. The data is extracted from documents and it is important that all data is presented on one page (pagination is not an option unfortunately).

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

Using jQuery, I make an ajax request to the server to retrieve the data. On completion of the request, I pass the data to an output function. The output function loops through the data array using a for loop and concatenating the rows to a variable. Once the looping is complete, the variable containing the table is then appended to an existing div on the page and then I go on to bind events to the table for working with the data.

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

With a small set of data (~1000-2000 rows) it works relatively good but some of the data sets contain upwards of 10,000 rows which causes Firefox to either crash and close or become unresponsive.

对于一小组数据(约 1000-2000 行),它工作得相对较好,但有些数据集包含超过 10,000 行,这会导致 Firefox 崩溃并关闭或变得无响应。

My question is, is there a better way to accomplish what I am doing?

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

Here's some code:

这是一些代码:

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

I was initially using a jQuery each loop but switched to the for loop which shaved off some ms.

我最初使用 jQuery each 循环,但切换到 for 循环,它减少了一些毫秒。

I thought of using something like google gears to try offloading some of the processing (if that's possible in this scenario).

我想过使用类似 google gears 的东西来尝试卸载一些处理(如果在这种情况下可能的话)。

Any thoughts?

有什么想法吗?

采纳答案by Neil

joinHi,

加入嗨,

The rendering is a problem, but there is also a problem with concatenating so many strings inside the loop, especially once the string gets very large. It would probably be best to put the strings into individual elements of an array then finally use "join" to create the huge string in one fell swoop. e.g.

渲染是一个问题,但在循环内连接这么多字符串也有问题,尤其是当字符串变得非常大时。最好将字符串放入数组的单个元素中,然后最终使用“join”一举创建巨大的字符串。例如

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(''));

Also, I would use the array indexing method shown here, rather than using "push" since, for all browsers except Google Chrome it is faster, according to this article.

此外,我将使用此处显示的数组索引方法,而不是使用“推送”,因为根据这篇文章,对于除 Google Chrome 之外的所有浏览器,它都更快。

回答by David Tang

Displaying that many rows is causing the browser's rendering engineto slow down, not the JavaScript engine. Unfortunately there's not a lot you can do about that.

显示这么多行会导致浏览器的渲染引擎变慢,而不是 JavaScript 引擎。不幸的是,您对此无能为力。

The best solution is to just not display so many rows at the same time, either through pagination, or virtual scrolling.

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

回答by Noel Walters

The way you are building your string will cause massive amounts of garbage collection.

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

As the string gets longer and longer the javascript engine has to keep allocating larger buffers and discarding the old ones. Eventually it will not be able to allocate sufficient memory without recycling the remains of all the old strings.

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

This problem gets worse as the string grows longer.

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

Instead try adding new elements to the DOM one at a time using the jQuery manipulation API

而是尝试使用jQuery 操作 API一次一个地向 DOM 添加新元素

Also consider only rendering what is visible and implement your own scrolling.

还要考虑只渲染可见的内容并实现自己的滚动。

回答by Imrul

You can do couple of things to increase the performance:

您可以做几件事来提高性能:

  1. your rowsvariable is getting bigger and bigger so, don't store the html in one variable. solution can be $.each()function and each function you append the element into DOM. But this is minor adjustment.
  2. Html generating is good, but you can try DOM creating and appending. Like $('<tr></tr>').
  3. And finally, this will solve your problem for sure : use multiple ajax call in the first ajax call collect how many data is available and fetch approximately 1,000 or may be more data. And use other calls to collect remaining data. If you want, you can use synchronous call or Asynchronous calls wisely.
  1. 您的变量越来越大,所以不要将 html 存储在一个变量中。解决方案可以是$.each()函数,并且每个函数都可以将元素附加到 DOM 中。但这是微调。
  2. Html 生成很好,但您可以尝试 DOM 创建和附加。喜欢$('<tr></tr>')
  3. 最后,这肯定会解决您的问题:在第一个 ajax 调用中使用多个 ajax 调用收集有多少可用数据并获取大约 1,000 或更多数据。并使用其他调用来收集剩余数据。如果需要,您可以明智地使用同步调用或异步调用。

But try to avoid storing the value. Your DOM size will be huge but it should work on moder browsers and forget about IE6.

但尽量避免存储该值。你的 DOM 大小会很大,但它应该可以在现代浏览器上工作,而不要考虑 IE6。

@fuel37 : Example

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

in this process you need to create & maintain id's or classes for manipulation. You have the control to bind events and manipulate each elements there.

在此过程中,您需要创建和维护用于操作的 id 或类。您可以控制绑定事件并操纵其中的每个元素。

回答by peteorpeter

Per others suggestions (I'm not reputable enough to comment yet, sorry!), you might try the TableSorter pluginto handle only displaying a usable amount of data at a time.

根据其他人的建议(我的信誉还不足以发表评论,抱歉!),您可以尝试使用TableSorter 插件来处理一次只显示可用数据量。

I don't know how it fares at very high numbers of rows, but their example data is 1000 rows or so.

我不知道它在行数非常多的情况下如何,但他们的示例数据是 1000 行左右。

This wouldn't help with JS performance but would keep the burden off the browser renderer.

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

回答by digiguru

Could try this...

可以试试这个...

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('');

This might squeeze an extra bit of performance...

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

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

回答by mplungjan

Answering to get formatting

回答以获取格式

What happens if you do

如果你这样做会发生什么

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