我想用异步JavaScript追加2000个表行。大约需要3秒钟,并且UI控件在页面内被阻止。我可以在没有阻塞的情况下附加到DOM吗?
如您所见,只需3-4秒后点击演示中的复选框:
https://jsfiddle.net/zencd/xpvt214o/16545/
演示内容:
function populateTable() {
for (var i = 0; i < 4*1000; i++) {
var tr = $('<tr></tr>');
...
tr.appendTo(table);
}
}
setTimeout(populateTable, 0); // async
答案 0 :(得分:1)
https://jsfiddle.net/759L1c90/
这个想法是以块的形式填充表,然后让UI在块之间更新。这允许用户与复选框进行交互并查看填充表格,而不是盯着具有无响应UI的空白页面。
您可以摆弄变量以找到可接受的用户体验。这个答案使用async/await,如果你不熟悉它们,我建议你学习它,因为它非常有用。这里的折衷是桌子需要更长的时间来填充,但这是值得的。
编辑:我建议将表格构建为一个字符串,只插入一次,因为它很快,就像其他答案一样。然而,这种异步“块”加载方法是一个很好的模式。
var t1 = new Date().getTime();
var log = console.log;
var table = $('#the-table');
var rows = 4000
var columns = 10
var chunk = 100
var timeout = 50
async function populateTable() {
let totalRows = rows
while(totalRows > 0){
await addChunk()
totalRows -= chunk
}
log('table populated at ', (new Date().getTime() - t1), 'ms');
}
function addChunk(){
return new Promise(resolve=>{
setTimeout(()=>{
for (var i = 0; i < chunk; i++) {
var tr = $('<tr></tr>');
for (var j = 0; j < columns; j++) {
var td = $('<td></td>');
td.text('' + i + '_' + j);
td.appendTo(tr);
}
tr.appendTo(table);
resolve()
}
},timeout)
})
}
setTimeout(populateTable, 0);
答案 1 :(得分:1)
首先使用vanilla JS(更快),并一次性插入HTML,而不是单独插入每行,td和文本数千次:
const t1 = new Date().getTime();
const log = console.log;
const table = document.querySelector('#the-table');
function populateTable() {
const trStrings = [];
for (let i = 0; i < 4000; i++) {
const tdValues = [];
for (let j = 0; j < 10; j++) {
tdValues.push(i + '_' + j);
}
trStrings.push('<tr><td>' + tdValues.join('</td><td>') + '</td></tr>');
}
table.innerHTML = trStrings.join('');
log('table populated at ', (new Date().getTime() - t1), 'ms');
}
setTimeout(populateTable, 0);
https://jsfiddle.net/xpvt214o/16614/
统计:
(索引):80表填充为79毫秒
(index):80表填充78 ms
(索引):80表填充为77毫秒
(索引):80表填充为73毫秒
(索引):80表填充为81毫秒
它在某种意义上仍然是“阻塞”,如果需要仍可以解决,但它的数量级更快,因此阻塞问题不再是一个问题。
但是,如果可能的话,这应该是通过服务器提供的,而不是使用Javascript即时解析和呈现。
答案 2 :(得分:0)
非阻塞的一个关键原因是应该在内存中创建TABLE。将TR插入内存表中。最后,完成循环后,将TABLE插入DOM。这是代码:
const generateTable = () => {
return new Promise((resolve) => {
const table = document.createElement('table');
const column = Array.from({ length: 4000}, (v, i) => i);
const row = Array.from({ length: 10}, (v, i) => i);
column.forEach((col, i) => {
const tr = document.createElement('tr');
row.forEach((r, j) => {
const td = document.createElement('td');
td.innerHTML = `${i}-${j}`;
tr.appendChild(td);
})
table.appendChild(tr);
})
table.id = 'the-table';
resolve(table);
})
}
const t1 = new Date().getTime();
generateTable()
.then(table => {
document.body.appendChild(table);
console.log('table populated at ', (new Date().getTime() - t1), 'ms');
})
答案 3 :(得分:0)
<tbody>
的子元素开始,创建需要插入表中的所有内容。将<tbody>
的内容附加到(4,000行+ 40,000个单元格)后,创建DocumentFragment并将<tbody>
附加到其中。 <table>
。这是唯一提交的DOM交互。 <tbody>
附加到<table>
,就可以在公园散步。 <tr>
和40,000 <td>
附加到<table>
代价很高。 </body>
结束标记之前,因此阻止不是问题。 ★第一次测试是@ 100ms,第二次测试是@ 80ms,第三次测试是@60ms
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<label>UI<input type="checkbox"></label>
<output id='msg'></output>
<table id="table01" border="1"></table>
<script>
var t0 = new Date().getTime();
var frag = document.createDocumentFragment();
var tb = document.createElement('tbody');
for (let i = 0; i < 4000; i++) {
const tr = document.createElement('tr');
for (let j = 0; j < 10; j++) {
const td = document.createElement('td');
td.textContent = `${i}_${j}`;
tr.appendChild(td);
}
tb.appendChild(tr);
}
frag.appendChild(tb);
document.getElementById('table01').appendChild(frag);
var t1 = new Date().getTime();
document.getElementById('msg').value = `time elapsed for insertion: ${t1 - t0}`;
</script>
</body>
</html>
&#13;