Yet, another complete and working example of how to make a scrolling table with a frozen column on the right side.
This example reunites the CSS techniques mentioned in this post and some basic JavaScript functionality for the row buttons using only one event handler.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
/* Center the grid container */
.container {
width: 800px;
height: 600px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
/* Grid style */
#grid {
margin-top: 25px;
position: relative;
left: 50%;
transform: translateX(-50%);
width: auto;
max-width: 90%;
min-width: 400px;
overflow: hidden;
height: auto;
max-height: 350px;
}
/* Table border */
table,
th,
td { border: 1px solid #c8c6c6; }
/* Table size */
table {
width: 100%;
height: 340px;
margin: 0 auto;
display: block;
overflow-x: auto;
border-spacing: 0;
}
tbody { white-space: nowrap; }
caption {
padding: 5px 10px;
font-weight: bold;
background: #929090;
border-bottom: 1px solid #c8c6c6;
position: sticky;
top: 0;
z-index: 2;
}
/* Columns style */
th,
td {
padding: 5px 10px;
border-top-width: 0;
border-left-width: 0;
}
th {
background: #929090;
vertical-align: bottom;
text-transform: capitalize;
}
th.fixed {
position: sticky;
right: 0;
z-index: 2;
}
td.fixed {
position: sticky;
right: 0;
z-index: 1;
background: #fff;
}
th:last-child,
td:last-child { border-right-width: 0; }
tr:last-child td { border-bottom-width: 0; }
/* Header & Footer row style */
thead tr {
position: sticky;
top: 29px;
z-index: 2;
}
tfoot tr {
position: sticky;
bottom: 0;
z-index: 2;
}
</style>
</head>
<body>
<div class="container">
<div id="grid">
<table>
<caption>This is the caption</caption>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="fixed">Actions</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr>
<th>Total</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="fixed"></th>
</tr>
</tfoot>
</table>
</div>
</div>
<script>
// Get some dummy data from the web.
fetch('https://random-data-api.com/api/v2/beers?size=25')
.then(res => res.json())
.then(res => {
let tHeaders = document.querySelectorAll('thead tr th')
let keys = Object.keys(res[0]);
// Manually pick some headers
tHeaders[0].innerText = keys[3] // name
tHeaders[1].innerText = keys[2] // brand
tHeaders[2].innerText = keys[4] // style
tHeaders[3].innerText = keys[5] // hop
tHeaders[4].innerText = keys[6] // yeast
tHeaders[5].innerText = keys[7] // malts
tHeaders[6].innerText = keys[9] // alcohol
// Add the dummy data to the table
let tBody = document.querySelector('tbody')
res.forEach(obj => {
tBody.innerHTML += `<tr>
<td>${obj.name}</td>
<td>${obj.brand}</td>
<td>${obj.style}</td>
<td>${obj.hop}</td>
<td>${obj.yeast}</td>
<td>${obj.malts}</td>
<td>${obj.alcohol}</td>
<td class='fixed'><button class='editBtn'>Edit</button> <button class='delBtn'>Delete</button></td>
</tr>`
})
// Add a click event handler to the grid.
document.getElementById('grid').addEventListener('click', e => {
let editBtn = e.target.closest('.editBtn')
let delBtn = e.target.closest('.delBtn')
let row = e.target.closest('tr')
// Check if 'Edit' button was clicked...
if (editBtn !== null) {
console.log(row.children[0].innerText + ' edition button clicked.')
}
// Check if 'Delete' was clicked...
if (delBtn !== null) {
console.log(row.children[0].innerText + ' deletion button clicked')
}
})
})
</script>
</body>
</html>