5

I'm trying to generate a table with 3 rows and 3 cells e.g.

<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>

The Javascript I'm using to do this is:

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>';
}

The HTML being:

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

However, when I run the code all the table elements end up in the first column, so instead of a 3x3 table I get a 1x9 table.

Anyone know what I'm doing wrong?

4
  • tab = document.getElementById('tab'); This does not exists.. Commented Feb 16, 2014 at 14:46
  • is the same thing happening when you save your tr/tds to a variable and write to tab only once? Commented Feb 16, 2014 at 14:46
  • @Pogrindis getElementById() is a function... Commented Feb 16, 2014 at 14:49
  • but the id element of tab does not exist when it is being called. Commented Feb 16, 2014 at 15:01

6 Answers 6

5

Option #1

You should use helper string variable for this task:

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;

Demo: http://jsfiddle.net/hrrKg/

Option #2

I would also recommend to make use of table 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;
    }
}

Demo: http://jsfiddle.net/hrrKg/1/


Basically why it happens. Whenever you set something like:

tab.innerHTML += '<tr>'

table content becomes not valid HTML so browser fixes it automatically making it look like something:

<tbody><tr></tr></tbody>

Further loops makes it even more confused hence a result.

Also take a look at cookie monster's answer, for it is more comprehensive to my mind.

Sign up to request clarification or add additional context in comments.

2 Comments

Ah you diamond, it works. Do you know why you have to do this? Or is it just one of those things?
When you set innerHTML property browser parses it and creates a DOM tree set of object. You can't just concatenate innerHTML reliable like if it was ordinary string.
5

You're treating the DOM like it's the HTML you write.

When you do

.innerHTML += "<tr>"

..you're creating an entire <tr></tr> element. There's no such thing as "half an element" in the DOM.


Then after that, when you += the <td> elements, they're now going after the <tr> you just created, which is invalid, so the browser corrects it as best it can.

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

And finally, when you do

.innerHTML += "</tr>"

you're simply adding invalid HTML, so it's probably ignored.


Furthermore, the use of .innerHTML += "...content..." is pretty bad. It first serializes the previous DOM nodes to HTML, destroys those DOM nodes, adds the new HTML to what it just serialized, and then parses that HTML to create (and recreate) the nodes.

So every iteration, you're destroying and recreating all that you created in all the iterations up to that point. This destruction is terribly inefficient and is unnecessary. Instead, build the entire string, and then use .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);

6 Comments

mmmm. Really informative. Thanks :) If you could provide citation, it would be awesome.
so writing the whole tr/td into a separate variable and only writing it to tab once should be the solution?
So each time innerHTML is set, the browser literally renders that? So OP is ending up with <tr></tr><td>01</td></tr>? I'll need to remember this one! haha
@thefourtheye: I don't really have anything to cite. The .innerHTML just basically parses HTML like it would when the page loads. So if the input it gets is <tr>, it isn't aware that more input is coming, so it renders it as best it can. Since a <tr> element doesn't require a closing tag, it just closes it.
@RonnyLindner: Mostly. The other issue is the use of += with .innerHTML, which is a horribly destructive approach. I've updated my answer with a solution that uses .insertAdjacentHTML() instead.
|
3

Alternative to string:

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

Comments

1

Problems you are adding <tr> once which is invalid HTML by itself and gets removed automatically. You can do:

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>';
}

5 Comments

OP is using += so that won't be happening.
the code says += and not =, this shouldn't delete the content
If that was the case I wouldn't get any output, however when I run the code I've got I'm given: 00 01 02 10 11 12 20 21 22
Problems you can't add just "<tr>" it needs to be both full html tag: "<tr></tr>"
@PetarVasilev, yeah I saw that in dfsq's answer.. didn't even know that happened ha! I normally use createElement so have never experimented with this before. Learn something new everyday...
1

I made some small adjustments and got it working in 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;

Comments

1

In the same vein as MamaWalter's answer, here is an alternative functional approach that uses DOM nodes rather than string concatenation:

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 for reference.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.