Further from this : thread
I want to have the left columns width and position dynamycally. Because it keeps changes when I adjust the header text.
I modified the html, full code is here
this script to identify left position of columns dynamycally:
document.addEventListener("DOMContentLoaded", function() {
const table = document.querySelector("table");
const headers = table.querySelectorAll("th");
const rows = table.querySelectorAll("tr");
let leftOffset = 0;
for (let i = 0; i < 3; i++) {
const columnWidth = headers[i].offsetWidth; // Get actual width of each column
// Apply sticky positioning to the first three columns
headers[i].style.position = "sticky";
headers[i].style.left = `${leftOffset}px`;
headers[i].style.zIndex = "10"; // Ensure row headers stay above regular cells
//headers[i].style.background = "#fff"; // Prevent overlapping transparency
rows.forEach(row => {
const cell = row.cells[i];
if (cell) {
cell.style.position = "sticky";
cell.style.left = `${leftOffset}px`;
cell.style.zIndex = "5";
cell.style.background = "#fff"; // Prevent content from showing through
}
});
leftOffset += columnWidth;
}
// Make the first row (column headers) sticky
const firstRow = table.querySelector("tr");
if (firstRow) {
firstRow.querySelectorAll("th").forEach(header => {
header.style.position = "sticky";
header.style.top = "0px"; // Stick to the top
header.style.zIndex = "15"; // Keep it above other headers but below intersections
//header.style.background = "#fff"; // Prevent overlap issues
});
}
// Fix the intersection where the first row AND first three columns meet
for (let i = 0; i < 3; i++) {
const cell = firstRow.cells[i];
if (cell) {
cell.style.zIndex = "20"; // Highest priority for top-left corner cells
cell.style.background = "#fff"; // Prevent layering issues
}
}
});
Not all the header rows and cols are sticked:
I think it's z-index is not right, but I do not get that.
The problem is : the second row header (or the second ), when the table is scrolled horizontally to the right, it overlaps (on the top of element) the columns on the left side, it supposed to be under the columns element.
this elements
<th colspan="2" class="weekend">Sat</th>
<th colspan="2" class="weekend">Sun</th>
<th colspan="2" class="">Mon</th>
<th colspan="2" class="">Tue</th>
<th colspan="2" class="">Wed</th>
<th colspan="2" class="">Thu</th>
<th colspan="2" class="">Fri</th>
<th colspan="2" class="weekend">Sat</th>
<th colspan="2" class="weekend">Sun</th>
<th colspan="2" class="">Mon</th>
<th colspan="2" class="">Tue</th>
<th colspan="2" class="">Wed</th>
<th colspan="2" class="">Thu</th>
<th colspan="2" class="">Fri</th>
<th colspan="2" class="weekend">Sat</th>
<th colspan="2" class="weekend">Sun</th>
<th colspan="2" class="">Mon</th>
<th colspan="2" class="">Tue</th>
<th colspan="2" class="">Wed</th>
<th colspan="2" class="">Thu</th>
<th colspan="2" class="">Fri</th>
<th colspan="2" class="weekend">Sat</th>
<th colspan="2" class="weekend">Sun</th>
<th colspan="2" class="">Mon</th>
<th colspan="2" class="">Tue</th>
<th colspan="2" class="">Wed</th>
<th colspan="2" class="">Thu</th>
<th colspan="2" class="">Fri</th>
<th colspan="2" class="weekend">Sat</th>
<th colspan="2" class="weekend">Sun</th>
<th colspan="2" class="">Mon</th>
overlaps this:
<th></th>
<th></th>
<th></th>
and the rows in the tbody when it scrolls vertically, it overlaps those <th></th>
too.
<table>
z-index:2;
.head
should have z-index: 1;
:root {
--colorAccent: #35c1ac;
}
* {
margin: 0;
box-sizing: border-box;
}
body {
font: 1rem/1.4 "Poppins", sans-serif;
}
.roster {
--cw: 80px;
/* Set desired Column width */
overflow: auto;
max-height: 200px;
display: flex;
font-size: 0.8rem;
.aside,
.main {
display: grid;
flex: 1;
height: min-content;
&>div {
height: 1.5rem;
padding: 0.2rem 0.6rem;
white-space: nowrap;
border-top: 1px solid #0001;
display: flex;
&.head {
display: flex;
position: sticky;
z-index: 1;
top: 0;
height: 3.5em;
background: #fff;
}
}
}
.aside {
grid-template-columns: repeat(3, auto);
background: #fff;
z-index: 2;
position: sticky;
left: 0;
}
.main {
background: 0px 0px / calc(var(--cw) * 2) var(--cw) repeating-linear-gradient(90deg, #0001 0px, #0001 var(--cw), #0000 var(--cw), #0000 calc(var(--cw) * 2));
&>div {
position: relative;
}
.head {
padding: 0;
.day {
padding: 0.2rem 0.6rem;
display: flex;
flex-direction: column;
align-items: center;
width: var(--cw);
background: #3b3b3b;
color: #fff;
text-transform: uppercase;
&.weekend {
background: var(--colorAccent);
}
}
}
.task {
position: absolute;
text-align: center;
background: var(--colorAccent);
border-radius: 1rem;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: calc(var(--cw) * var(--duration));
left: calc(var(--cw) * var(--start) - calc(var(--cw) / 2));
}
}
}
<div class="roster">
<div class="aside">
<div class="head">#</div><div class="head">RoomType</div><div class="head">Status</div>
<div>11302</div><div>K1ORS</div><div>DIRTY</div>
<div>11303</div><div>K1ORS</div><div>DIRTY</div>
<div>1304</div><div>K1ORS</div><div>DIRTY</div>
<div>1305</div><div>K1ORS</div><div>CLEAN</div>
<div>1306</div><div>K1ORS</div><div>CLEAN</div>
<div>1307</div><div>K1ORS</div><div>INSPECTED</div>
<div>1308</div><div>K1ORS</div><div>CLEAN</div>
<div>1309</div><div>K1</div><div>DIRTY</div>
<div>1310</div><div>T2</div><div>INSPECTED</div>
<div>1311</div><div>K1RRD</div><div>CLEAN</div>
<div>1312</div><div>T2</div><div>DIRTY</div>
<div>1313</div><div>T2</div><div>DIRTY</div>
<div>1314</div><div>K1</div><div>CLEAN</div>
<div>1318</div><div>K1ORS</div><div>CLEAN</div>
<div>1319</div><div>K1ORS</div><div>CLEAN</div>
</div>
<div class="main">
<div class="head">
<div class="day weekend"><span class="d-w">01-Mar</span><span>Sat</span></div>
<div class="day weekend"><span class="d-w">02-Mar</span><span>Sun</span></div>
<div class="day"><span class="d-w">03-Mar</span><span>Mon</span></div>
<div class="day"><span class="d-w">04-Mar</span><span>Tue</span></div>
<div class="day"><span class="d-w">05-Mar</span><span>Wed</span></div>
<div class="day"><span class="d-w">06-Mar</span><span>Thu</span></div>
<div class="day"><span class="d-w">07-Mar</span><span>Fri</span></div>
<div class="day weekend"><span class="d-w">08-Mar</span><span>Sat</span></div>
<div class="day weekend"><span class="d-w">09-Mar</span><span>Sun</span></div>
<div class="day"><span class="d-w">10-Mar</span><span>Mon</span></div>
<div class="day"><span class="d-w">11-Mar</span><span>Tue</span></div>
<div class="day"><span class="d-w">12-Mar</span><span>Wed</span></div>
<div class="day"><span class="d-w">13-Mar</span><span>Thu</span></div>
<div class="day"><span class="d-w">14-Mar</span><span>Fri</span></div>
<div class="day weekend"><span class="d-w">15-Mar</span><span>Sat</span></div>
<div class="day weekend"><span class="d-w">16-Mar</span><span>Sun</span></div>
<div class="day"><span class="d-w">17-Mar</span><span>Mon</span></div>
<div class="day"><span class="d-w">18-Mar</span><span>Tue</span></div>
<div class="day"><span class="d-w">19-Mar</span><span>Wed</span></div>
<div class="day"><span class="d-w">20-Mar</span><span>Thu</span></div>
<div class="day"><span class="d-w">21-Mar</span><span>Fri</span></div>
<div class="day weekend"><span class="d-w">22-Mar</span><span>Sat</span></div>
<div class="day weekend"><span class="d-w">23-Mar</span><span>Sun</span></div>
<div class="day"><span class="d-w">24-Mar</span><span>Mon</span></div>
<div class="day"><span class="d-w">25-Mar</span><span>Tue</span></div>
<div class="day"><span class="d-w">26-Mar</span><span>Wed</span></div>
<div class="day"><span class="d-w">27-Mar</span><span>Thu</span></div>
<div class="day"><span class="d-w">28-Mar</span><span>Fri</span></div>
<div class="day weekend"><span class="d-w">29-Mar</span><span>Sat</span></div>
<div class="day weekend"><span class="d-w">30-Mar</span><span>Sun</span></div>
<div class="day"><span class="d-w">31-Mar</span><span>Mon</span></div>
</div>
<div>
<div class="task" style="--start:3; --duration:2;">Paula Martin</div>
<div class="task" style="--start:6; --duration:5;">Fiona Jackson</div>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div>
<div class="task" style="--start:1; --duration:2;">Michael White</div>
</div>
<div></div>
<div>
<div class="task" style="--start:1; --duration:5;">George Thomas</div>
</div>
<div>
<div class="task" style="--start:7; --duration:2;">Emma Thompson</div>
</div>
</div>
</div>
In case you want to dynamically generate the days with JS:
const el = (sel, par = document) => par.querySelector(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "short", // i.e: "Mon"
month: "short", // i.e: "Feb"
});
const elDays = el(".roster .main .head");
const createDaysForMonth = (year, month) => {
const daysInMonth = new Date(year, month, 0).getDate();
for (let day = 1; day <= daysInMonth; day++) {
const date = new Date(year, month - 1, day);
const d = String(day).padStart(2, "0");
const [w, m] = formatter.format(date).split(" ");
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
const elDay = elNew("div", {
className: `day${isWeekend ? " weekend" : ""}`,
innerHTML: `<span class="d-w">${d}-${w}</span><span>${m}</span>`,
});
elDays.append(elDay);
}
}
// Create days
createDaysForMonth(2025, 3);
:root {
--colorAccent: #35c1ac;
}
* {
margin: 0;
box-sizing: border-box;
}
body {
font: 1rem/1.4 "Poppins", sans-serif;
}
.roster {
--cw: 80px;
/* Set desired Column width */
overflow: auto;
max-height: 200px;
display: flex;
font-size: 0.8rem;
.aside,
.main {
display: grid;
flex: 1;
height: min-content;
&>div {
height: 1.5rem;
padding: 0.2rem 0.6rem;
white-space: nowrap;
border-top: 1px solid #0001;
display: flex;
&.head {
display: flex;
position: sticky;
z-index: 1;
top: 0;
height: 3.5em;
background: #fff;
}
}
}
.aside {
grid-template-columns: repeat(3, auto);
background: #fff;
z-index: 2;
position: sticky;
left: 0;
}
.main {
background: 0px 0px / calc(var(--cw) * 2) var(--cw) repeating-linear-gradient(90deg, #0001 0px, #0001 var(--cw), #0000 var(--cw), #0000 calc(var(--cw) * 2));
&>div {
position: relative;
}
.head {
padding: 0;
.day {
padding: 0.2rem 0.6rem;
display: flex;
flex-direction: column;
align-items: center;
width: var(--cw);
background: #3b3b3b;
color: #fff;
text-transform: uppercase;
&.weekend {
background: var(--colorAccent);
}
}
}
.task {
position: absolute;
text-align: center;
background: var(--colorAccent);
border-radius: 1rem;
color: #fff;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: calc(var(--cw) * var(--duration));
left: calc(var(--cw) * var(--start) - calc(var(--cw) / 2));
}
}
}
<div class="roster">
<div class="aside">
<div class="head">#</div><div class="head">RoomType</div><div class="head">Status</div>
<div>11302</div><div>K1ORS</div><div>DIRTY</div>
<div>11303</div><div>K1ORS</div><div>DIRTY</div>
<div>1304</div><div>K1ORS</div><div>DIRTY</div>
<div>1305</div><div>K1ORS</div><div>CLEAN</div>
<div>1306</div><div>K1ORS</div><div>CLEAN</div>
<div>1307</div><div>K1ORS</div><div>INSPECTED</div>
<div>1308</div><div>K1ORS</div><div>CLEAN</div>
<div>1309</div><div>K1</div><div>DIRTY</div>
<div>1310</div><div>T2</div><div>INSPECTED</div>
<div>1311</div><div>K1RRD</div><div>CLEAN</div>
<div>1312</div><div>T2</div><div>DIRTY</div>
<div>1313</div><div>T2</div><div>DIRTY</div>
<div>1314</div><div>K1</div><div>CLEAN</div>
<div>1318</div><div>K1ORS</div><div>CLEAN</div>
<div>1319</div><div>K1ORS</div><div>CLEAN</div>
</div>
<div class="main">
<div class="head"></div>
<div>
<div class="task" style="--start:3; --duration:2;">Paula Martin</div>
<div class="task" style="--start:6; --duration:5;">Fiona Jackson</div>
</div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div>
<div class="task" style="--start:1; --duration:2;">Michael White</div>
</div>
<div></div>
<div>
<div class="task" style="--start:1; --duration:5;">George Thomas</div>
</div>
<div>
<div class="task" style="--start:7; --duration:2;">Emma Thompson</div>
</div>
</div>
</div>