I am using HTML, CSS, and JS to create a page to display my CSV data neatly. The table being used to display the CSV data is inside a flexbox container div. Currently, the max height of the container is set to 95vh
, and once the table starts the exceeds the height, it makes the entire div container scrollable.
However, I am only looking to make the table data itself scrollable (i.e. <tbody>
), not the entire container. This effectively means I want to keep my <h2>
header and <thead>
header, inside the <div>
container, stationery while making the table scrollable if it exceeds the max height of the container.
I have looked at several past SO posts to make only the table content scrollable, and attempted CSS methods such as display: block
and position: sticky
, but it does not achieve the desired effect I intend for.
If there is a duplicate post to mine (that exactly solves my issue), please link me to it, as I have extensively searched for duplicates. And I would like to solve this issue using pure HTML/CSS/JS or with the help of external scripts.
MY CODE (you can also tinker with my JSFiddle)
// Define CSV data here
const testData = `Value1A,Value1B,Value1C
Value2A,Value2B,Value2C
Value3A,Value3B,Value3C
Value4A,Value4B,Value4C
Value5A,Value5B,Value5C
Value6A,Value6B,Value6C
Value7A,Value7B,Value7C
Value8A,Value8B,Value8C
Value9A,Value9B,Value9C
Value10A,Value10B,Value10C
Value11A,Value11B,Value11C
Value12A,Value12B,Value12C
Value13A,Value13B,Value13C
Value14A,Value14B,Value14C
Value15A,Value15B,Value15C`;
// Split the CSV data
let rows = testData.split("n");
// Add table tags to each row and cell
let table = "";
rows.forEach(function(row) {
const columns = row.split(/,(?=(?:[^"]*"[^"]*")*(?![^"]*"))/);
table += `<tr>`;
// Iterate over each column
columns.forEach(function(column) {
table += "<td>" + column.replace(/^"(.*)"$/, "$1") + "</td>";
});
table += "</tr>";
});
// Update the table body
const tbody = document.querySelector("#csv-table tbody");
tbody.innerHTML = table;
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #F3F2F1;
background-color: #d9efa1;
}
.container {
display: flex;
flex-direction: column;
justify-content: center;
max-width: 1024px;
margin: 0 auto;
padding: 5%;
}
.csv {
background-color: #FFFFFF;
border-radius: 10px;
padding: 3%;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
max-height: 95vh;
overflow: auto;
}
.csv h2 {
margin-top: 1%;
margin-bottom: 3%;
margin-left: 0.1%;
color: #f6b40e;
}
.csv table {
width: 100%;
border-collapse: collapse;
table-layout: fixed;
}
.csv table thead th {
background-color: #f6b40e;
color: #FFFFFF;
padding: 3%;
text-align: left;
}
.csv table tbody tr:nth-child(even) {
background-color: #F3F2F1;
}
.csv table tbody td {
padding: 3%;
word-wrap: break-word;
}
<!DOCTYPE html>
<html>
<head>
<title>CSV</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<div class="csv">
<h2>CSV</h2>
<table id="csv-table">
<thead>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
</tr>
</thead>
<tbody>
<!-- Dynamically generated rows will be added here -->
</tbody>
</table>
</div>
</div>
</body>
</html>
3
Answers
We add selectors for
tbody
,thead tr
andth, td
and usedisplay: block
.You should be able to use
position: sticky
with an appropriatetop
value. You may also wish to consider having some sort of background color to prevent the scrolled values showing above the table header.To explain the
top
values I’ve used here:calc(min(1024px, 100vw) * 0.0084)
is derived frommargin-top: 1%
is1%
of the width of the containing box. However, the percentage fortop
uses the height so we cannot applytop: 1%
, so instead, we need to calculate whatmargin-top: 1%
is:min(1024px, 100vw)
is the.container
‘s width, which is implicitly 100% of the viewport,100vw
up to itsmax-width: 1024px
, the1024px
.0.0084
is derived from:padding
of.container
of5%
each, that is0.05
each side.padding
of.csv
of3%
each, that is0.03
each side.1%
margin-top
itself, equal to0.01
.For
calc((min(1024px, 100vw) * 0.0336) + 1.75em)
, it is mostly the same:min(1024px, 100vw)
is the.container
‘s width, which is implicitly 100% of the viewport,100vw
up to itsmax-width: 1024px
, the1024px
.0.0084
is derived from:padding
of.container
of5%
each, that is0.05
each side.padding
of.csv
of3%
each, that is0.03
each side.1%
margin-top
of the<h2>
, equal to0.01
.3%
margin-bottom
of the<h2>
, equal to0.03
.1.75em
comes from theline-height
of the<h2>
text.If you want to be super accurate, we’d need to use JavaScript. This is because non-overlay scrollbars can give us slightly different values, and we do not have the necessary values in CSS alone to calculate it properly.
As explained by Wongjn: position sticky can help.
But you can also wrap your
<table>
in a<div>
with a max-height and overflow auto and give your<thead>
a sticky position like so:We need this additional wrap because a sticky parent element with overflow auto or scroll will disable the sticky behavior.