javascripthtmlcsshtml-table

Javascript generated table creates non-square table cells


I'm trying to construct a table dynamically, with a size determined by the current width of the browser window.

Since the table cells' sizes and their containing div's size are both being set relative to the window's width, the cells should fit exactly within the div. This is working perfectly for the width, but not for the height. The last row of cells appears to overhang the bottom of the div by a fractional amount.

Logging the sizes of the div and one of the table cells shows that they ought to align correctly, as the cell's height is exactly 1/10 of the div's height, and there are 10 rows of cells. However, the visual doesn't match, even though the math works out as expected.

What is causing the discrepancy between the calculated and displayed heights?

const pageWidth = document.documentElement.scrollWidth;
const nRows = 10; const nColumns = 10;
const tableDiv = document.getElementById("tableDiv");
tableDiv.style.width = 0.7*pageWidth + "px";
tableDiv.style.height = 0.7*pageWidth + "px";
tableDiv.style.backgroundColor = "yellow";
        
// Dynamically generate a table of the specified dimensions
function constructTable(nR, nC) {
    const tbl = document.createElement("table"); tbl.id = "test table";
    var tRow; var tCell;
    for (let r = 0; r < nR; r++) { // Use 'let,' not 'var,' or else all row/column numbers will be set to the final (maximum) values of r and c
        tRow = document.createElement("tr");
        tbl.appendChild(tRow);
        for (let c = 0; c < nC; c++) {
            tCell = document.createElement("td"); tCell.className = "testing"; tRow.appendChild(tCell);
        }
    }
    return tbl;
}
        
function showTable(t) {
    tableDiv.innerHTML = "";
    tableDiv.appendChild(t);
    const dynamicSizeCells = document.getElementsByClassName("testing");
    for (var i = 0; i < dynamicSizeCells.length; i++) {
        dynamicSizeCells[i].style.width = 0.7*pageWidth/nColumns + "px";
        dynamicSizeCells[i].style.height = 0.7*pageWidth/nRows + "px";
        dynamicSizeCells[i].style.backgroundColor = "green";
    }
    console.warn("Div width is " + tableDiv.style.width + ", div height is " + tableDiv.style.height + "; cell width is " + dynamicSizeCells[0].style.width + ", cell height is " + dynamicSizeCells[0].style.height);
}
        
const theTable = constructTable(nRows, nColumns);
showTable(theTable);
body {
    background-color: darkblue;
}
#tableDiv {
    margin-left: 15%;
    margin-top: 5%;
}
.testing {
    background-color: "green";
}
<div id="tableDiv"></div>


Solution

  • The issue lies within the border-spacing. By default, a 2px border spacing exists between adjacent cells. You have to factor this into your calculation by subtracting two from the final height (starting from the second row).

    Result:

    const pageWidth = document.documentElement.scrollWidth;
    const nRows = 10; const nColumns = 10;
    const tableDiv = document.getElementById("tableDiv");
    tableDiv.style.width = 0.7*pageWidth + "px";
    tableDiv.style.height = 0.7*pageWidth + "px";
    tableDiv.style.backgroundColor = "yellow";
         
            
    // Dynamically generate a table of the specified dimensions
    function constructTable(nR, nC) {
        const tbl = document.createElement("table"); tbl.id = "test table";
        var tRow; var tCell;
        for (let r = 0; r < nR; r++) { // Use 'let,' not 'var,' or else all row/column numbers will be set to the final (maximum) values of r and c
            tRow = document.createElement("tr");
            tbl.appendChild(tRow);
            for (let c = 0; c < nC; c++) {
                tCell = document.createElement("td"); tCell.className = "testing"; tRow.appendChild(tCell);
            }
        }
        return tbl;
    }
            
    function showTable(t) {
        tableDiv.innerHTML = "";
        tableDiv.appendChild(t);
        const dynamicSizeCells = document.getElementsByClassName("testing");
        for (var i = 0; i < dynamicSizeCells.length; i++) {
            dynamicSizeCells[i].style.width = 0.7*pageWidth/nColumns + "px";
            
            // only subtract 2 starting from the second row!
            dynamicSizeCells[i].style.height = 0.7*pageWidth/nRows + (i > nRows * (nColumns - 1) ? 0 : -2) + "px";
            dynamicSizeCells[i].style.backgroundColor = "green";
        }
       //console.warn("Div width is " + tableDiv.style.width + ", div height is " + tableDiv.style.height + "; cell width is " + dynamicSizeCells[0].style.width + ", cell height is " + dynamicSizeCells[0].style.height);
    }
            
    const theTable = constructTable(nRows, nColumns);
    showTable(theTable);
    body {
        background-color: darkblue;
    }
    #tableDiv {
        margin-left: 15%;
        margin-top: 5%;
    }
    
    
    .testing {
        background-color: "green";
        box-sizing: border-box;
    }
    <div id="tableDiv"></div>


    So why doesn't this affect the table horizontally?

    It does. However, since the table-layout is set to auto, the cell widths are automatically shrunk to fit the width of the table.

    If we set the table-layout to fixed, you will see the effect happen on both axes.

    I've minified the code so it won't take up half the screen; it's just a demonstration anyways.

    const pageWidth=document.documentElement.scrollWidth,nRows=10,nColumns=10,tableDiv=document.getElementById("tableDiv");function constructTable(t,l){var n,a,o=document.createElement("table");o.id="test table",o.style.width=.7*pageWidth+"px";for(let e=0;e<t;e++){n=document.createElement("tr"),o.appendChild(n);for(let e=0;e<l;e++)(a=document.createElement("td")).className="testing",n.appendChild(a)}return o}function showTable(e){tableDiv.innerHTML="",tableDiv.appendChild(e);for(var t=document.getElementsByClassName("testing"),l=0;l<t.length;l++)t[l].style.width=.7*pageWidth/nColumns+"px",t[l].style.height=.7*pageWidth/nRows+"px",t[l].style.backgroundColor="green"}tableDiv.style.width=.7*pageWidth+"px",tableDiv.style.height=.7*pageWidth+"px",tableDiv.style.backgroundColor="yellow";const theTable=constructTable(nRows,nColumns);showTable(theTable);
    table{table-layout:fixed}body{background-color:#00008b}#tableDiv{margin-left:15%;margin-top:5%}.testing{background-color:"green"}
    <div id="tableDiv"></div>