Issue
I have a spreadsheet-style grid. The root looks like this:
<div id="grid">
<div id="left-column"></div>
<div id="center-column"></div>
<div id="right-column"></div>
<div>
The left and right columns are each 1 column but the center column is N
number of columns.
When scrolling left and right, I want to lock the left and right column in place so they are always visible. And only scroll-x the center columns.
However when scrolling vertically, I want to lock the first cell in all columns (the header cell) so they are always visible.
I have this almost working:
// JS is only used to generate the cells
const cells = Array.from({ length: 20 }).map(c => c = `${Math.floor(Math.random() * 200) + 50}px`)
cells[0] = '120px'
cells[19] = '120px'
const first = cells.shift()
const last = cells.pop()
const rows = Array.from({ length: 100 })
const grid = document.getElementById("grid")
const leftColumn = document.getElementById("left-column")
const centerColumn = document.getElementById("center-column")
const rightColumn = document.getElementById("right-column")
grid.style.display = "grid"
grid.style.gridTemplateColumns = `${first} 1fr ${last}`
function makeItem(text) {
const div = document.createElement("div")
div.classList.add('cell')
const newContent = document.createTextNode(text);
div.appendChild(newContent);
return div;
}
function makeRow() {
const innerGrid = document.createElement("div")
innerGrid.style.display = "grid"
innerGrid.style.gridTemplateColumns = cells.join(' ')
cells.forEach((cell, index) => {
const child = makeItem(`cell ${index + 1}`)
innerGrid.appendChild(child)
})
const a = makeItem('First')
const z = makeItem('Last')
leftColumn.appendChild(a)
centerColumn.appendChild(innerGrid)
rightColumn.appendChild(z)
}
rows.forEach(() => {
makeRow()
})
.cell {
background: #eee;
font-family: Arial;
border: 0.5px solid black;
color: #333;
height: 30px;
display: grid;
align-items: center;
text-align: center;
}
#grid {
width: min(100%, 1000px);
max-height: 300px;
overflow: scroll;
position: relative;
border: 0.5px solid black;
}
#center-column {
overflow-x: scroll;
}
#left-column > .cell, #right-column > .cell, #center-column > :first-child > .cell {
font-weight: bold;
color: black;
background: #aaa;
color: #eee;
}
#left-column > :first-child,
#right-column > :first-child,
#center-column > :first-child,
#center-column > :first-child > .cell{
position: sticky;
top: 0;
background: #d70000;
}
#center-column {
position: relative;
}
<div id="grid">
<div id="left-column"></div>
<div id="center-column"></div>
<div id="right-column"></div>
<div>
However when you scroll vertically, the left and right column headers stay locked but the center headers do not.
I know why. This is because in order to achieve the horizontal scrolling behavior I want, I need to make the center column have overflow scroll. This kills the position sticky that is making the headers lock in place (just for the center columns).
So I know the problem causing it not to work. But I cannot find a pure css work around. I can use JavaScript to make this work but I am trying to find a pure css solution.
I can use whatever nested html elements and styling are needed I just want a pure css solution.
Solution
You don't need to create physical wrappers DIVs. Just create your cells, place them inside your #grid element and play with CSS and the :nth-child()
pseudo class selector and make the desired cols, rows sticky
at top
left
right
respectively.
Here's an example of a 6 columns grid:
// This JS is only used to dynamically generate the cells.
const el = (sel, par) => (par || document).querySelector(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const repeat = (n, cb) => [...Array(n)].forEach((_, i) => cb(i));
const elGrid = el("#grid");
repeat(90, (i) => {
elGrid.append(elNew("div", {
className: "cell",
textContent: `Cell ${i+1}`
}));
});
body {
font-family: Arial;
}
#grid {
max-height: 180px;
overflow-y: scroll;
position: relative;
display: grid;
grid-template-columns: repeat(6, 1fr);
.cell {
border: 1px solid black;
text-align: center;
padding: 0.5rem;
min-width: 20vw;
/* leftmost column */
&:nth-child(6n-5) {
background: #ddd;
position: sticky;
left: 0;
}
/* rightmost column */
&:nth-child(6n) {
background: #ddd;
position: sticky;
right: 0;
}
/* top row */
&:nth-child(-n+6) {
background: #aaa;
position: sticky;
top: 0;
z-index: 1;
}
/* first cell */
&:nth-child(1) {
z-index: 2;
}
}
}
<div id="grid"></div>
Answered By - Roko C. Buljan
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.