Динамическое создание большой таблицы html в javascript производительности
У меня есть приложение, которое используется для анализа данных, и у меня возникает несколько проблем с производительностью при создании таблицы. Данные извлекаются из документов, и важно, чтобы все данные были представлены на одной странице (к сожалению, разбиение на страницы недоступно).
Используя 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, который сбрил несколько мс.
Я думал использовать что-то вроде google gears, чтобы попытаться выгрузить часть обработки (если это возможно в этом сценарии).
Любые мысли?
Ответы
Ответ 1
joinHi,
Реализация - проблема, но есть также проблема с конкатенацией так много строк внутри цикла, особенно когда строка становится очень большой. Вероятно, лучше всего поместить строки в отдельные элементы массива, а затем использовать "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(''));
Кроме того, я бы использовал приведенный здесь метод индексирования массива, вместо того, чтобы использовать "push", поскольку для всех браузеров, кроме Google Chrome, это быстрее, согласно этой статьи.
Ответ 2
Отображение того, что многие строки заставляют механизм рендеринга браузера замедляться, а не механизм JavaScript. К сожалению, вы не можете многое сделать с этим.
Лучшим решением является просто не отображать столько строк одновременно, либо путем разбивки на страницы, либо виртуальной прокрутки.
Ответ 3
То, как вы строите свою строку, приведет к огромному количеству сбора мусора.
По мере того, как строка становится длиннее и длиннее, механизм javascript должен продолжать выделять большие буферы и отбрасывать старые. В конце концов, он не сможет выделить достаточную память, не перерабатывая остатки всех старых строк.
Эта проблема ухудшается по мере увеличения длины строки.
Вместо этого попробуйте добавить новые элементы в DOM по одному с помощью jQuery API манипуляции
Также рассмотрите только то, что видно, и выполните собственную прокрутку.
Ответ 4
Вы можете сделать несколько вещей, чтобы повысить производительность:
- переменная строка становится все больше и больше, поэтому не храните html в одной переменной. решение может быть
$.each()
функцией и каждой функцией, которую вы добавляете в элемент DOM. Но это незначительная корректировка.
- Генерация Html хороша, но вы можете попробовать создать и добавить DOM. Как
$('<tr></tr>')
.
- И, наконец, это решит вашу проблему наверняка: используйте несколько вызовов ajax в первом вызове ajax, чтобы собрать, сколько данных доступно, и получить около 1000 или может быть больше данных. И используйте другие вызовы для сбора оставшихся данных. Если вы хотите, вы можете использовать синхронный вызов или асинхронные вызовы с умом.
Но старайтесь избегать сохранения значения. Размер 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);
});
}
в этом процессе вам нужно создать и поддерживать id или классы для манипуляций. У вас есть элемент управления для привязки событий и управления ими.
Ответ 5
Ответ на получение форматирования
Что произойдет, если вы сделаете
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
Ответ 6
В других предложениях (я недостаточно авторитет, чтобы комментировать, извините!), вы можете попробовать плагин TableSorter для обработки только отображения полезное количество данных за раз.
Я не знаю, как это работает при очень большом количестве строк, но их пример данных составляет 1000 строк или около того.
Это не помогло бы с JS-производительностью, но уберет бремя от браузера.
Ответ 7
Мог бы попробовать это...
Улучшить циклы
Улучшить 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="';