Dynamic HTML Table with Fixed Header and Fixed First Column

Static/sticky Header Using Dynamic Table

  • Remove <body onload="initialisePage('LW')"> and use DOMContentLoaded instead as it happens much sooner than the document load event.

    • load is only fired after ALL resources/content has been loaded, including "non-essential" (non-DOM) content like images and external content like ad-banners, which means the load event may be fired tens-of-seconds after DOMContentLoaded which makes the load event kinda useless today).
  • Change your CSS to this:

    table > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 10;
    }

    table > tbody > tr.floated {
    color: '#ffffff';
    background-color: 'blue';
    }

    table > tbody > tr.broken {
    background-color: '#00ff00';
    }
  • JavaScript uses camelCase for functions, values (variables and parameters) and properties, not PascalCase.

  • Avoid var and use const and let in scripts where appropriate instead. Note that const means "unchanging reference" (kinda like C++); it does not mean "immutable" or "compile-time constant value". I think this definition of const was a mistake by the JavaScript language designers, but that's just, like, my opinion, man.
  • Use CSS classes via classList instead of setting individual style properties using .style.
  • The current JavaScript ecosystem also generally uses 1TBS instead of the Allman style.
  • Prefer === (exactly-equals) instead of == (equals) because JavaScript's type coercion can be surprising).
  • Avoid using innerHTML wherever possible. Use .textContent for setting normal text content (and avoid using .innerText too). Misuse of innerHTML leads to XSS vulnerabilities.
  • It's 2020. STOP USING JQUERY!!!!!!!!!!

    • Cite
    • Cite
    • Cite
    • Cite
  • DONT USE ALL-CAPS IN YOUR JAVASCRIPT COMMENTS BECAUSE IT LOOKS LIKE THE AUTHOR IS SHOUTING AT YOU NEEDLESSLY AND IT GETS QUITE ANNOYING FOR OTHER READERS ARRRRGGGHHHHH
  • You need to handle HTTP request responses correctly (e.g. to check for succesful responses with the correct Content-Type).
  • Avoid using j as an iterable variable name because it's too visually similar to i.
  • Change your JavaScript to this:

    <script>
    // You should put all of your own application-specific top-level page script variables in their own object so you can easily access them separately from the global `window` object.
    const myPageState = {
    loop : null,
    fileName: null,
    table : null
    };
    window.myPageState = myPageState; // In the top-level function, `const` and `let`, unlike `var`, do not create a global property - so you need to explicitly set a property like so: `window.{propertyName} = ...`.

    window.addEventListener( 'DOMContentLoaded', onDOMLoaded );

    function onDOMLoaded( ev ) {

    window.myPageState.fileName = "LW";
    window.myPageState.loop = setInterval( refreshTable, 500 );
    }

    async function refreshTable() {

    if( typeof window.myPageState.fileName !== 'string' || window.myPageState.fileName.length === 0 ) return;

    const url = "http://10.142.32.72/dashboard/" + window.myPageState.fileName + ".json";

    const resp = await fetch( url );
    if( resp.status === 200 && resp.headers['ContentType'] === 'application/json' ) {
    const deserialized = await resp.json();
    ceateAndPopulateTableFromJSONResponse( deserialized );
    }
    else {
    // Error: unexpected response.
    // TODO: error handling
    // e.g. `console.error` or `throw new Error( "Unexpected response." )`, etc.
    }
    }

    function ceateAndPopulateTableFromJSONResponse( myBooks ) {

    // TODO: Verify the `myBooks` object layout (i.e. schema-verify `myBooks`).

    const columnTitles = ["Page", "Slug", "Pres 1", "Pres 2", "CAM", "Format", "Clip Dur", "Total", "Backtime"];
    const columnNames = ["page-number", "title", "pres1", "pres2", "camera", "format", "runs-time", "total-time", "back-time"];

    const table = window.myPageState.table || document.createElement( 'table' );
    if( window.myPageState.table !== table ) {
    window.myPageState = table;
    document.getElementById("showData").appendChild( table );
    }

    // Create the <thead>, if nnecessary:
    if( table.tHead === null )
    {
    table.tHead = document.createElement( 'thead' );

    const tHeadTR = table.tHead.insertRow(-1);
    for( let i = 0; i < columnNames.length; i++ ) {
    const th = document.createElement('th');
    th.textContent = columnTitles[i];
    tHeadTR.appendChild( th );
    }
    }

    // Clear any existing tbody:
    while( table.tBodies.length > 0 ) {
    table.removeChild( table.tBodies[0] );
    }

    // Populate a new <tbody>:
    {
    const tbody = document.createElement('tbody');

    for( let i = 0; i < myBooks.length; i++ ) {

    const tr = table.insertRow(-1);
    tr.classList.toggle( 'floated', myBooks[i]["floated"] === "true" );
    tr.classList.toggle( 'broken' , myBooks[i]["break" ] === "true" && myBooks[i]["floated"] === "false" );

    for( let c = 0; c < columnNames.length; c++ ) {
    const td = tr.insertCell(-1);
    const colName = columnNames[c];
    td.textContent = myBooks[i][ colName ];
    }
    }

    table.appendChild( tbody );
    }

    console.log( "Refreshed: " + window.myPageState.fileName );
    }
    </script>

How to Create HTML table with fixed header but dynamic column

You want something like this:

HTML

<div id="table">
<table>
<thead>
<tr>
<th>Column A</th>
<th id="flex-header">Column B</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<button value="3">3</button>
<button value="7">7</button>

JS

$(function () {
$("button").click(function (e) {
var cols = $(this).val();

// You'll want to do something here to get the column data
var data = $("<tr></tr>").append($("<td>Col 0</td>"));
for (i = 0; i < cols; i++) {
data.append($("<td>Col " + (i + 1) + "</td>"));
}
$("#flex-header").prop("colspan", cols);
$("#table table tbody").html("").append(data);
});
});

jsfiddle

This will allow you to change around the number of columns easily. Of course, there are other ways to do it (like the other answer with toggling tbody elements) but this should give you a little flexibility with the column counts.

EDIT
Here is an updated jsfiddle with the table toggle behavior.

CSS - HTML Table with dynamic width and ellipsis in header

If you are able to add an extra wrapper you can do like below. Using contain: inline-size you tell the browser to ignore its content when defining the inline-size (the width in your case).

table,
tr > * {
border: 1px solid;
}

span {
display: block;
contain: inline-size; /* the content won't affect the size */
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
<table>
<thead>
<tr>
<th><span>Col 1</span></th>
<th><span>Column 2 header</span></th>
</tr>
</thead>
<tbody>
<tr>
<td>Column 1 content</td>
<td>Column 2</td>
</tr>
</tbody>
</table>

How do I create an HTML table with a fixed/frozen left column and a scrollable body?

If you want a table where only the columns scroll horizontally, you can position: absolute the first column (and specify its width explicitly), and then wrap the entire table in an overflow-x: scroll block. Don't bother trying this in IE7, however...

Relevant HTML & CSS:

table {
border-collapse: separate;
border-spacing: 0;
border-top: 1px solid grey;
}

td,
th {
margin: 0;
border: 1px solid grey;
white-space: nowrap;
border-top-width: 0px;
}

div {
width: 500px;
overflow-x: scroll;
margin-left: 5em;
overflow-y: visible;
padding: 0;
}

.headcol {
position: absolute;
width: 5em;
left: 0;
top: auto;
border-top-width: 1px;
/*only relevant for first row*/
margin-top: -1px;
/*compensate for top border*/
}

.headcol:before {
content: 'Row ';
}

.long {
background: yellow;
letter-spacing: 1em;
}
<div>
<table>
<tr>
<th class="headcol">1</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
<tr>
<th class="headcol">2</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
<tr>
<th class="headcol">3</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
<tr>
<th class="headcol">4</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
<tr>
<th class="headcol">5</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
<tr>
<th class="headcol">6</th>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
<td class="long">QWERTYUIOPASDFGHJKLZXCVBNM</td>
</tr>
</table>
</div>


Related Topics



Leave a reply



Submit