JavaScript中的FOR循环行为奇怪

5
我正在尝试生成一个具有3行和3个单元格的表格 例如。
<table>
<tr>
   <td> 00 </td>    <td> 01 </td>    <td> 02 </td>
</tr>
<tr>
   <td> 10 </td>    <td> 11 </td>    <td> 12 </td>
</tr>
<tr>
   <td> 20 </td>    <td> 21 </td>    <td> 22 </td>
</tr>
</table>

我用于此的JavaScript代码如下:
tab = document.getElementById('tab');
for(i=0; i<3; i++)
{ 
 tab.innerHTML += '<tr>';

  for(j=0; j<3; j++) 
  {
   tab.innerHTML += '<td id="'+i+''+j+'">'+i+''+j+'</td>'; 
  }

 tab.innerHTML += '</tr>';
}

HTML如下:

<table id="tab">
</table>

然而,当我运行这段代码时,所有表格元素都出现在第一列,所以我得到了一个1x9的表格,而不是3x3的表格。

有人知道我做错了什么吗?


tab = document.getElementById('tab'); 这个不存在。 - Pogrindis
当您将tr/td保存到变量并仅一次写入tab时,情况是否相同? - Ronny Lindner
@Pogrindis getElementById() 是一个函数... - Michael Coxon
但是在调用时,选项卡的id元素不存在。 - Pogrindis
6个回答

5
你把DOM当作你所写的HTML来处理。
当你这么做时,
.innerHTML += "<tr>"

当你创建一个完整的<tr></tr>元素时, 在DOM中不存在"半个元素"。


此后,当你对<td>元素进行+=操作时,它们会被放置在你刚刚创建的<tr>元素之后,这是无效的,所以浏览器会尽其所能地进行更正。

// This is going after the <tr></tr>
tab.innerHTML += '<td id="'+i+''+j+'">'+i+''+j+'</td>'; 

最后,当您进行操作时,务必注意:
.innerHTML += "</tr>"

你只是在添加无效的 HTML,所以它可能会被忽略。


此外,使用 .innerHTML += "...content..." 是相当糟糕的。它首先将先前的 DOM 节点序列化为 HTML,然后销毁这些 DOM 节点,将新的 HTML 添加到刚刚序列化的内容中,然后解析该 HTML 以创建(和重新创建)节点。

因此,在每次迭代中,您都会摧毁并重建自始至终所有迭代中创建的所有内容。这种破坏非常低效且不必要。相反,先构建整个字符串,然后使用.insertAdjacentHTML()

tab = document.getElementById('tab');

var html = ""
for(i=0; i<3; i++)
{ 
 html += '<tr>';

  for(j=0; j<3; j++) 
  {
   html += '<td id="'+i+''+j+'">'+i+''+j+'</td>'; 
  }

 html += '</tr>';
}

tab.insertAdjacentHTML("beforeend", html);

1
非常有用的信息,谢谢 :) 如果您能提供引用,那就太棒了。 - thefourtheye
那么将整个tr/td写入单独的变量中,然后仅在tab中写入一次,这应该是解决方案吗? - Ronny Lindner
那么每次设置 innerHTML,浏览器都会直接渲染它吗?所以 OP 最终得到的是 <tr></tr><td>01</td></tr>?我需要记住这个!哈哈 - Michael Coxon
1
@thefourtheye:我没有什么可以引用的。.innerHTML 基本上只是像页面加载时一样解析 HTML。因此,如果它接收到的输入是 <tr>,它不知道还有更多的输入,所以尽可能地呈现它。由于 <tr> 元素不需要闭合标签,因此它只会关闭它。 - cookie monster
@RonnyLindner: 大部分是可以的。另一个问题是在使用.innerHTML时使用+=,这是一种非常破坏性的方法。我已经更新了我的答案,使用了.insertAdjacentHTML()来解决这个问题。 - cookie monster
@MichaelCoxon:基本上是这样的。它是一个DOM,因此不会以任何HTML结束。只有节点。因此,最后的</tr>很可能被忽略处理。 - cookie monster

5

选项 #1

您应该为此任务使用帮助字符串变量:

var tab = document.getElementById('tab'),
    str = '';

for (var i = 0; i < 3; i++) {
    str += '<tr>';

    for (var j = 0; j < 3; j++) {
        str += '<td id="' + i + '' + j + '">' + i + '' + j + '</td>';
    }

    str += '</tr>';
}

tab.innerHTML = str;

演示:http://jsfiddle.net/hrrKg/

选项 #2

我还建议利用表格API。

var tab = document.getElementById('tab');

for (var i = 0; i < 3; i++) {
    var row = tab.insertRow(i);
    for (var j = 0; j < 3; j++) {
        var cell = row.insertCell(j);
        cell.id = i + '' + j;
        cell.innerHTML = i + '' + j;
    }
}

演示:http://jsfiddle.net/hrrKg/1/
基本上是为什么会发生这种情况。每当你设置类似以下的内容时:
tab.innerHTML += '<tr>'
表格内容不符合HTML标准,因此浏览器会自动修复,使其看起来像这样:
<tbody><tr></tr></tbody>

进一步的循环会使结果更加混乱。

同时,请查看饼干怪兽的回答,因为在我看来它更全面。


啊,你真是个钻石,它起作用了。你知道为什么要这样做吗?还是说这只是其中一件事情? - Simeon
当您设置innerHTML属性时,浏览器会解析它并创建一组DOM树对象。您不能像普通字符串那样可靠地连接innerHTML。 - dfsq

3

字符串的替代方案:

tab = document.getElementById('tab');
for(i=0; i<3; i++)
{ 
 var tr = document.createElement("tr");

  for(j=0; j<3; j++) 
  {
    var td = document.createElement("td");
    td.id = i+''+j;
    td.innerHTML= i+''+j
    tr.appendChild(td); 
  }

 tab.appendChild(tr);
}

FIDDLE


1
您正在添加一个无效的HTML标签,即<tr>,这会导致该标签被自动删除。您可以采用以下方式:
tab = document.getElementById('tab');
for(i=0; i<3; i++)
{ 
 var html = '<tr>';

  for(j=0; j<3; j++) 
  {
   html += '<td id="'+i+''+j+'">'+i+''+j+'</td>'; 
  }

 tab.innerHTML += html + '</tr>';
}

2
OP正在使用+=,所以那不会发生。 - Michael Coxon
代码中写的是 += 而不是 =,这不应该删除内容。 - Ronny Lindner
如果那是情况的话,我将不会得到任何输出,但是当我运行我的代码时,我得到了以下内容:000102101112202122 - Simeon
1
问题是你不能只添加“<tr>”,它必须是完整的HTML标签:“<tr></tr>”。 - Petar Vasilev
1
@PetarVasilev,是的,我在dfsq的回答中看到了那个..哈哈,我甚至不知道发生了什么!我通常使用createElement,所以以前从未尝试过这个。每天都学到新东西... - Michael Coxon

1
我进行了一些小调整,并在Fiddler中使其正常工作:
tab = document.getElementById('tab');

var result = "<tbody>";
for(i=0; i<3; i++)
{ 
 result += '<tr>';

  for(j=0; j<3; j++) 
  {
   result += '<td id="'+i+''+j+'">'+i+''+j+'</td>'; 
  }

 result += '</tr>'; 
}
result += "</tbody>";

console.log(result)
tab.innerHTML = result;

1

与MamaWalter的答案类似,这里提供一种使用DOM节点而非字符串拼接的替代功能性方法:

var tab = document.getElementById('tab');

var bind = Function.prototype.bind,
    $tr = bind.call(Document.prototype.createElement, document, "tr"),
    $td = bind.call(Document.prototype.createElement, document, "td"),
    $text = bind.call(Document.prototype.createTextNode, document),
    appender = function(parent, child) {
      parent.appendChild(child);
      return parent;
    },
    wrapWith = function(fn, child) { return appender(fn(), child); },
    wrapWithTd = wrapWith.bind(undefined, $td);

var outer = [1,2,3], inner = [1,2,3];

outer.map(function(row) {
  function createCellContents(col) {
    return $text(row+ '' + col);
  }

  return inner
    .map(createCellContents)
    .map(wrapWithTd)
    .reduce(appender, $tr());
}).reduce(appender, tab);

jsbin 作为参考。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接