使用JavaScript非阻塞地附加DOM元素

时间:2018-04-01 01:27:53

标签: javascript dom

我想用异步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

4 个答案:

答案 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)

哇,我迟到了。作为一个类似的答案,我使用vanilla JS,承诺和函数式编程来更快地绘制表格。

非阻塞的一个关键原因是应该在内存中创建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)

  1. 如果您想快速加载某些内容,请不要使用jQuery,请使用纯JavaScript。
  2. 如果您正在与DOM进行交互,请执行一次,而不是44,000次。
  3. 通过从<tbody>的子元素开始,创建需要插入表中的所有内容。将<tbody>的内容附加到(4,000行+ 40,000个单元格)后,创建DocumentFragment并将<tbody>附加到其中。
  4. 此时浏览器已经在@ 100ms的范围内执行了44,002个语句作为SO上的Stack Snippet或@ 60ms 作为Plunker上的Plunk 但是浏览器还没有触及DOM。
  5. 要执行的最后一个语句是将docFrag附加到<table>。这是唯一提交的DOM交互。
  6. 在DOM中,只需将一个<tbody>附加到<table>,就可以在公园散步。
  7. 在DOM中,将{4} <tr>和40,000 <td>附加到<table>代价很高。
  8. 请注意,脚本位于</body>结束标记之前,因此阻止不是问题。
  9. 第一次测试是@ 100ms,第二次测试是@ 80ms,第三次测试是@60ms

    演示 - 查看此Plunk以获得更快的负载

    &#13;
    &#13;
    <!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;
    &#13;
    &#13;