2

我需要显示由某些数字配置创建的视觉模式,以及它们之间的关系。为此,我创建了一个表并使用行/列作为 x/y 坐标(以 ID 编码)。对于许多 TD 元素,我提供了有意义的背景颜色并附加了 mouseover/out 处理程序以显示一个带有该 TD 分配的编号和有关它的信息的弹出窗口。(单元格太小而无法实际包含数字;它们更像是一种视觉设备,我使用上述背景颜色,因此可以很容易地看到图案。)

我需要能够放大和缩小此表,以及以其他方式对其进行管理。但是,在处理大型表(1000 行和列,或更多)时,浏览器需要花费大量时间在每个新的缩放级别呈现它。做了一些研究,我发现 TABLE 元素需要更多的汁液,因为浏览器必须根据表格中的单元格内容和可用宽度重新计算。将'table-layout'设置为'fixed'并给表格一个宽度应该可以防止这种情况发生,但它仍然需要很长时间。

然后我认为用 DIV 元素制作一个表格可能会起作用,因为这将需要更少的总元素并且没有像表格那样的自动调整大小 - 我可以对尺寸进行硬编码。我尝试了一些我在网上找到的代码,使用“display: table”(以前从未听说过),以及非表格化的 DIV(喜欢编造词......)。

这是我创建的一个测试页面,用于测试这三个并为它们计时(在此处查看):

<!--
  Only tested in Google Chrome on Windows 7!
  I have not bothered testing in other browsers as this is a private project
  and all members of the project use Chrome.
-->
<html>
<head>
<title>Table Test</title>
<style>
#controls td {
  font-family: Courier New, monospace;
}
input[type=button] {
  width: 100%;
}
input[type=text] {
  width: 50px;
}
/* styles the pure table version */
table {
  table-layout: fixed;
  border-collapse: collapse;
}
.td {
  border: 1px solid black;
  width: 48px;
  height: 48px;
  vertical-align: top;
}
/* styles the DIV version using "display: table" */
.div-table {
  display: table;
  border: solid black;
  border-width: 1px 0px 0px 1px;
}
.div-table-row {
  display: table-row;
  width: auto;
  clear: both;
}
.div-table-col {
  float: left;
  display: table-column;
  width: 48px;
  height: 48px;
  border: solid black;
  border-width: 0px 1px 1px 0px;
}
/* styles the pure DIV version */
div#grid {
  border: solid black;
  border-width: 1px 0px 0px 1px;
}
.cell {
  display: inline-block;
  width: 49px;
  height: 49px;
  padding: 0px;
  margin: 0px;
  border: solid black;
  border-width: 0px 1px 1px 0px;
}
</style>

<script type="text/javascript">
function Stopwatch() {   // basic Stopwatch object to time functions
  var startTime = null; 
  var stopTime = null; 
  var running = false; 

  function getTime() {
    var day = new Date();
    return day.getTime();
  }

  this.start = function() { 
    if (running == true)
      return;
    else if (startTime != null) 
      stopTime = null; 
    running = true;    
    startTime = getTime();
    return startTime;
  }
  this.stop = function() { 
    if (running == false)
      return;    
    stopTime = getTime();
    running = false; 
    return stopTime;
  }
  this.duration = function() { 
    if (startTime == null || stopTime == null)
      return 'Undefined';
    else
      return (stopTime - startTime) / 1000;
  }
}
var stopwatch = new Stopwatch();

var x = 50;  // global dimension variables
var y = 100;

function updateDims() { // updates the global dimension variables
                        // returns true/false to indicate success
  x = parseInt(document.getElementById('x').value);
  y = parseInt(document.getElementById('y').value);
  if (typeof x == 'number' && x > 0 && typeof y == 'number' && y > 0) return true;
  return false;
}

function makeTableTable() {        // creates the pure table version
  if (!updateDims()) return false; // grab desired x/y dimensions

// grab count of how many times function has been run to calculate average
  var count = document.getElementById('count1');
  count = count.value = parseInt(count.value) + 1;
// grab previous average time to calculate new average
  var avg = parseFloat(document.getElementById('avg1').innerHTML);

// start the stopwatch, grabbing initial start time, and begin concatenating
  var start = stopwatch.start();
  var html = '<table id="grid" cellpadding="0" cellspacing="0">';
  for (var i = 0; i < y; i++) {  // for each row...
    html += '<tr>';
    for (var j = 0; j < x; j++) {  // build desired number of cells
      html += '<td class="td" id="'+j+'/'+[y-[i+1]]+'">' + [1+j+i*x] + '</td>';
    }
    html += '</tr>';  // close row
    j = 0;
  }
  html += '</table>';

// stop the watch and record duration it took to concatenate
  stopwatch.stop();
  document.getElementById('concat1').innerHTML = stopwatch.duration();

// start the watch again and insert HTML
  stopwatch.start();
  document.getElementById('output').innerHTML = html;

// stop the watch, grabbing the final end time, and record duration for innerHTML
  var end = stopwatch.stop();
  document.getElementById('insert1').innerHTML = stopwatch.duration();

// find total time from initial start time and final end time
  document.getElementById('total1').innerHTML = (end - start) / 1000;

// calculate average time
  if (count > 1)
    document.getElementById('avg1').innerHTML = Math.round(100000*(avg*(count-1)+(end-start)/1000)/count)/100000;
  else
    document.getElementById('avg1').innerHTML = (end-start) / 1000;
}

function makeDivTable() {          // creates the DIV version using "display: table"
  if (!updateDims()) return false;

  var count = document.getElementById('count2');
  count = count.value = parseInt(count.value) + 1;
  var avg = parseFloat(document.getElementById('avg2').innerHTML);

  var start = stopwatch.start();
  var html = '<div id="grid" class="div-table">';
  for (var i = 0; i < y; i++) {
    html += '<div class="div-table-row">';
    for (var j = 0; j < x; j++) {
      html += '<div class="div-table-col" id="'+j+'/'+[y-[i+1]]+'">' + [1+j+i*x] + '</div>';
    }
    html += '</div>';
    j = 0;
  }
  html += '</div>';

  stopwatch.stop();
  document.getElementById('concat2').innerHTML = stopwatch.duration();

  stopwatch.start();
  document.getElementById('output').innerHTML = html;

  var end = stopwatch.stop();
  document.getElementById('insert2').innerHTML = stopwatch.duration();
  document.getElementById('total2').innerHTML = (end - start) / 1000;

  if (count > 1)
    document.getElementById('avg2').innerHTML = Math.round(100000*(avg*(count-1)+(end-start)/1000)/count)/100000;
  else
    document.getElementById('avg2').innerHTML = (end-start) / 1000;

// update width of outer DIV
  document.getElementById('grid').style.width = 50 * x + 'px';
}

function makeDivDiv() {            // creates the pure DIV version
  if (!updateDims()) return false;
  var cells = x*y;   // will iterate through total number of cells,
                     // rather than by row and column
  var count = document.getElementById('count3');
  count = count.value = parseInt(count.value) + 1;
  var avg = parseFloat(document.getElementById('avg3').innerHTML);

  var start = stopwatch.start();
  var html = '<div id="grid">';
  for (var i = 0; i < cells; i++) {
    var id = [i-x*Math.floor(i/x)+1] + '/' + [y-Math.floor(i/x)];
    html += '<div class="cell" id="' + id + '">' + [i+1] + '</div>';
  }
  html += '</div>';

  stopwatch.stop();
  document.getElementById('concat3').innerHTML = stopwatch.duration();

  stopwatch.start();
  document.getElementById('output').innerHTML = html;

  var end = stopwatch.stop();
  document.getElementById('insert3').innerHTML = stopwatch.duration();
  document.getElementById('total3').innerHTML = (end - start) / 1000;

  if (count > 1)
    document.getElementById('avg3').innerHTML = Math.round(100000*(avg*(count-1)+(end-start)/1000)/count)/100000;
  else
    document.getElementById('avg3').innerHTML = (end-start) / 1000;

  document.getElementById('grid').style.width = 50 * x + 'px';
}

</script>

</head>
<body>

<table id="controls" border="1">
  <tr>
    <td>x: <input type="text" id="x" value="50"  /></td>
    <td>y: <input type="text" id="y" value="100" /></td>
    <td colspan="7"></td>
  </tr>
  <tr>
    <td><input type="button" onclick="makeTableTable()" value="Standard table" />
        <input type="hidden" id="count1" value="0" /></td>
    <td>Concatenation:</td>
    <td id="concat1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td> 
    <td>Insertion:</td>
    <td id="insert1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Total:</td>
    <td id="total1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Average:</td>
    <td id="avg1">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
  </tr>
  <tr>
    <td><input type="button" onclick="makeDivTable()" value="Div w/ 'display:table'" />
        <input type="hidden" id="count2" value="0" /></td>
    <td>Concatenation:</td>
    <td id="concat2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td> 
    <td>Insertion:</td>
    <td id="insert2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Total:</td>
    <td id="total2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Average:</td>
    <td id="avg2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
  </tr>
  <tr>
    <td><input type="button" onclick="makeDivDiv()" value="Div Table" />
        <input type="hidden" id="count3" value="0" /></td>
    <td>Concatenation:</td>
    <td id="concat3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Insertion:</td>
    <td id="insert3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Total:</td>
    <td id="total3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    <td>Average:</td>
    <td id="avg3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
  </tr>
</table><br />

<div id="output"></div>
</body>
</html>

首先尝试使用较小的值,例如默认值。然后尝试将 x 和 y 更改为 1000,并尝试默认浏览器缩放功能。生成表格以及放大或缩小都需要相当长的时间。

不过,有趣的是,据报道所用的时间显然不是它实际所用的时间——人们可以通过计算棒球场的秒数来看出这一点(很好的“一个密西西比州”)。当报告的几次运行后的平均时间约为 2.3、2.7 和 2.4 时,400x400 的网格分别持续大约 13 秒、5 秒和 7 秒。所有与时间相关的代码都不会对其产生太大影响。您可以将其全部删除或将其全部注释掉(查看),它是相同的(对我来说是 13、5 和 7)。

所以两个问题:

首先,为什么这些数字有差异?计时器应该对函数中发生的所有事情进行计时。除了第一次调用 updateDims 之外,发生的主要两件事是字符串连接和对 innerHTML 的赋值。(网格的宽度也发生了变化,但我看不出这会花费多少时间。)它只是浏览器渲染时间,在函数完成后发生,所以没有计时?支持这一点的一些证据是纯表格版本的明显时间要长得多,这意味着浏览器可能正在重新计算所有 TD 宽度等。

其次,可以做些什么呢?任何事物?串联是其中的一小部分,所以这不是问题。(而且我已经尝试推入一个数组并加入,并且连接始终更快,至少对我来说是这样。)此外,如果要相信秒表,将它插入到 innerHTML 不会花费那么长时间。这就是后来发生的任何事情。我曾考虑过使用后台程序技术,因此即使需要一段时间来更新,至少它不会一直锁定浏览器并且会进行一些明显的活动。但是如果锁定点发生在函数的“外部”,我看不出它是如何工作的。我可以设计自己的缩放功能,但我不相信它会比浏览器更快。初步测试说没有。

我正在尝试做的任何其他选择(尽管它可能对你来说很模糊;对不起)当然值得赞赏!

 

StackOverflow 上的其他一些页面:

大型 html 表慢吗?听起来好像表格通常可以快速渲染。但是,这可能只适用于已经硬编码到 HTML 文件中的表格,而不是像我的那样生成的。

创建大表的 Javascript 性能与我的情况非常相似,除了具有分页表的公认解决方案对我来说不是一个选项,因为这会破坏目的。而且我的表不包含数据。然而,其中一位受访者确实暗示,对于如此大的表,DOM 方法可能比 HTML 解析更快(尽管我在其他地方看到相反的情况;可能有合理数量的元素)。当我有机会时,我可能会测试一下。你怎么看?

4

0 回答 0