2

I am trying to create a table where each cell consists of a letter and a number. see an image of the target table:

target table

Here is the HTML code that I am using. It's a simple button that when clicked, should call the populateTable() function and then display the table on the page.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>Document</title>
    <style>
     table { border-collapse: collapse; margin: 1em 0; }
     td { border: 1px solid black; padding: 0.3em; }
    </style>
    </head>
    <body>
    
    <h2>Table</h2>
    <table id="the-table"></table>
    <button type="button" onclick="populateTable();">Populate table</button> 
    
    <script src="table.js"></script>
    </body>
</html>

But the main issue here is my JS code.

function populateTable(){
    letters = ["a", "b", "c", "d", "e"]
    numbers = ["1", "2", "3", "4", "5"]
    
    for(let i = 0; i < numbers.length; i++){
        document.getElementById("the-table").innerHTML += "<tr>"
        for(let j = 0; j < letters.length; j++){
            document.getElementById("the-table").innerHTML += "<td>" + letters[j] + numbers[i] + "</td>"
        }
        document.getElementById("the-table").innerHTML += "</tr>"
    }
}

How can I create a new <tr> after inner loop exits?

The outcome of this current code looks like this

Please help :'D

I tried moving around the

document.getElementById("the-table").innerHTML += "<tr>"

and

document.getElementById("the-table").innerHTML += "</tr>"

but to no avail. I tried looking for an answer online, but I found nothing that is useful to my case. things like appendChild were not really what i am looking for exactly. I just want to know why these lines mentioned above ("<tr>") won't work and what's the easiest way to fix this?

aloisdg
  • 22,270
  • 6
  • 85
  • 105
Mofro
  • 25
  • 6
  • 2
    "sorry for asking a stupid question, I am new to JavaScript". There is no stupid question. Only badly written ones and duplicates. Yours is quite fine actually. You show some efforts. Have fun learning JavaScript :) – aloisdg Nov 16 '22 at 13:49
  • Thank you very much. I've asked a question before, and my emotions were not spared. But I really appreciate your encouraging comment. – Mofro Nov 16 '22 at 15:08

3 Answers3

3

You can use creatElement instead of writing text. InnerHtml can be problematic security wise. Look into XSS if you are curious.

const letters = ["a", "b", "c", "d", "e"]
const numbers = ["1", "2", "3", "4", "5"]

const table = document.getElementById("the-table");

function populateTable() {
  for (let i = 0; i < numbers.length; i++) {
    const row = document.createElement("tr");
    for (let j = 0; j < letters.length; j++) {
      const column = document.createElement("td");
      column.textContent = letters[j] + numbers[i];
      row.appendChild(column);
    }
    table.appendChild(row);
  }
}
table {
  border-collapse: collapse;
  margin: 1em 0;
}

td {
  border: 1px solid black;
  padding: 0.3em;
}
<h2>Table</h2>
<table id="the-table"></table>
<button type="button" onclick="populateTable();">Populate table</button>

Your original logic was good but innerHTML is troublesome. If you inspect the html you will see that with innerHtml:

<table id="the-table"><tbody><tr></tr></tbody><tbody><tr><td>a1</td></tr></tbody><tbody><tr><td>b1</td></tr></tbody><tbody><tr><td>c1</td></tr></tbody><tbody><tr><td>d1</td></tr></tbody><tbody><tr><td>e1</td></tr></tbody><tbody><tr></tr></tbody><tbody><tr><td>a2</td></tr></tbody><tbody><tr><td>b2</td></tr></tbody><tbody><tr><td>c2</td></tr></tbody><tbody><tr><td>d2</td></tr></tbody><tbody><tr><td>e2</td></tr></tbody><tbody><tr></tr></tbody><tbody><tr><td>a3</td></tr></tbody><tbody><tr><td>b3</td></tr></tbody><tbody><tr><td>c3</td></tr></tbody><tbody><tr><td>d3</td></tr></tbody><tbody><tr><td>e3</td></tr></tbody><tbody><tr></tr></tbody><tbody><tr><td>a4</td></tr></tbody><tbody><tr><td>b4</td></tr></tbody><tbody><tr><td>c4</td></tr></tbody><tbody><tr><td>d4</td></tr></tbody><tbody><tr><td>e4</td></tr></tbody><tbody><tr></tr></tbody><tbody><tr><td>a5</td></tr></tbody><tbody><tr><td>b5</td></tr></tbody><tbody><tr><td>c5</td></tr></tbody><tbody><tr><td>d5</td></tr></tbody><tbody><tr><td>e5</td></tr></tbody></table>

If you really want to use innerHTML, there is an answer showcasing how. Don't do it if you can. A topic on the subject: innerHTML is adding tbody, why?. Their recommendation is the same as mine "You shouldn't really be building up a table with innerHTML operations anyways."

aloisdg
  • 22,270
  • 6
  • 85
  • 105
2

The issue here is that everytime you call += on the table element it creates a new tbody. So all the TDs will show up on a new line. You need to create a variable and create the entire inner table structure and then add it to your table like so. If you inspect the elements after running your code you can see all the tbodies. I hope this helps.

It should be noted, this is not the "proper" way to do this. I'm just providing the fix for the method you chose, as that's how people learn and grow :)

function populateTable(){
    letters = ["a", "b", "c", "d", "e"]
    numbers = ["1", "2", "3", "4", "5"]
    innerTable = '';
    
    for(let i = 0; i < numbers.length; i++){
        innerTable += "<tr>"
        
        for(let j = 0; j < letters.length; j++){
            innerTable += "<td>" + letters[j] + numbers[i] + "</td>"
        }
        
        innerTable += "</tr>"
    }
    
    document.getElementById("the-table").innerHTML += innerTable;
}
Gauge
  • 36
  • 3
  • 1
    Thank you very much. This is exactly what I was looking for. And your explanation as to why this works and why my code didn't is really good. I am learning JavaScript at the moment, and I do not intend to use this method for real-world applications but it's always a plus to know best practices when coding. thank you :'). – Mofro Nov 16 '22 at 15:50
0

<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>Document</title>
    <style>
     table { border-collapse: collapse; margin: 1em 0; }
     td { border: 1px solid black; padding: 0.3em; }
    </style>
    </head>
    <body>
    
    <h2>Table</h2>
    <table id="the-table"></table>
    <button type="button" onclick="populateTable();">Populate table</button> 
    
    <script >
    function populateTable(){
    letters = ["a", "b", "c", "d", "e"]
    numbers = ["1", "2", "3", "4", "5"]
    let str=""
    numbers.map(n=>{
        str+="<tr>"
        letters.map(l=>{
            str+=`<td> ${l} ${n} </td>`
        })
        str+="</tr>"
    })
        //console.log('str ',str);
        document.getElementById("the-table").innerHTML=str;
    }

    </script>
    </body>
</html>

By using ES6 you can use the nested map to iterate the array. Then add to the string after every iteration. Here is the newly created populateTable() function for you.

function populateTable(){
    letters = ["a", "b", "c", "d", "e"]
    numbers = ["1", "2", "3", "4", "5"]
    let str=""
    numbers.map(n=>{
        str+="<tr>"
        letters.map(l=>{
            str+=`<td> ${l} ${n} </td>`
        })
        str+="</tr>"
    })
        console.log('str ',str);
        document.getElementById("the-table").innerHTML=str;
    } 
  • You mixed `map` and `forEach`. You are mutating `str` while you could push to an array and concat or join. And the end this is still relying on innerHTML when it should not[.](https://www.meme-arsenal.com/memes/4694c22bcc1903ee4c4ef2adeae3d6eb.jpg) – aloisdg Nov 16 '22 at 14:24
  • what is the benefit of pushing to an array and then concat the array, compared to mutating `str`. – Shakhawat Hossain SHIHAB Nov 16 '22 at 17:08