I've seen similar issues addressed here and other places, but while Safari v14 seemed to fix many similar issues, the one addressed here remains a problem:
Working with basic CSS and HTML, I'm using a div with a "display: contents;" style to group elements that belong to a row in a css grid. I want to add a class to certain "filtered" rows that sets "display: none !important;" to the row element, thus hiding all the items in that row on the grid. Removing that class should re-show the previously filtered rows.
This technique works perfectly in Chrome and Firefox, but Safari shows some really strange behavior -- it doesn't resize the remaining rows properly when the "display: none" class is added to the filtered rows and it doesn't re-show the rows when that class is removed.
The example below tries to fully display this issue. Please run the example in "full page" mode. (In fact, checking it here, it seems to ONLY work in full page mode, and I can't explain that). You can use Chrome or Firefox and compare it with Safari. I'm hoping a workaround is available:
function addRows() {
var grid = document.getElementById("grid-div");
if (grid.childNodes.length < 10) {
for (var iRow = 0; iRow < 50; iRow++) {
var row = document.createElement("div");
row.classList.add("grid-row");
row.id = `row-${iRow}`;
grid.appendChild(row);
for (var iCol = 0; iCol < 3; iCol++) {
var label = document.createElement("label");
label.innerHTML = `Cell ${iRow}:${iCol}`;
row.appendChild(label);
}
}
} else {
alert("Looks like the grid was already filled");
}
}
function hideSomeRows() {
var grid = document.getElementById("grid-div");
if (grid.childNodes.length > 10) {
for (var iRow = 10; iRow < 40; iRow++) {
var rowToHide = document.getElementById(`row-${iRow}`);
rowToHide.classList.add("collapsed");
}
} else {
alert("First you need to fill the grid")
}
}
function showSomeRows() {
var grid = document.getElementById("grid-div");
if (grid.childNodes.length > 10) {
for (var iRow = 10; iRow < 40; iRow++) {
var rowToHide = document.getElementById(`row-${iRow}`);
rowToHide.classList.remove("collapsed");
}
} else {
alert("First you need to fill the grid")
}
}
function hideGrid() {
var grid = document.getElementById("grid-div");
grid.classList.add("collapsed");
}
function showGrid() {
var grid = document.getElementById("grid-div");
grid.classList.remove("collapsed");
}
.body {
overflow: auto;
border: 2px blue dashed;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: stretch;
padding: 0;
margin: 0;
height: 100vh;
width: 100vw;
box-sizing: border-box;
}
.main-div {
display: flex;
flex-direction: row;
overflow: auto;
}
.column-div {
display: flex;
flex-direction: column;
justify-items: flex-start;
align-items: flex-start;
overflow: auto;
}
/* Add some quick spacing */
.main-div div {
margin-bottom: 0.4rem;
}
.main-div button {
margin-right: 0.2rem;
}
.test-grid {
display: grid;
grid-template-columns: 5rem 5rem 5rem;
row-gap: 0.3rem;
column-gap: 0.2rem;
justify-items: stretch;
align-items: stretch;
border: 1px solid black;
/* just to see it */
overflow: auto;
}
.test-grid .grid-row {
display: contents;
}
.test-grid .grid-row label {
border: 1px purple solid;
}
.collapsed {
display: none !important;
}
<body>
<div class="body">
<div class="main-div">
<div class="column-div" style="flex: none;">
<div style="display: flex; flex: none;">
<button onclick="addRows()">Fill Grid</button>
<button onclick="hideGrid()">Hide Grid</button>
<button onclick="showGrid()">Show Grid</button>
</div>
<div style="display: flex; flex: none;">
<button onclick="hideSomeRows()">Hide Some Rows</button>
<button onclick="showSomeRows()">Show Some Rows</button>
</div>
<div id="grid-div" class="test-grid">
<div class="grid-row">
<label>Column 1</label>
<label>Column 2</label>
<label>Column 3</label>
</div>
</div>
</div>
<div class="column-div" style="margin-left: 1rem;">
<label>
<b>Please use full page mode!</b><br>
I believe this is a Safari glitch where changing an element inside a grid from
"display: content" to "display: none" and back is not handled properly. Here's how to
show this using the example to the left:
</label>
<ul>
<li>
First, click "Fill Grid" to add 50 rows (with display: content) to the grid.
<ul>
<li>
If your display is short enough, the grid will go off the bottom of the display and overflow will allow you to scroll through the rows without scrolling the rest of the content.
</li>
</ul>
</li>
<li>
Next, click "Hide Some Rows" to add a class that sets "display: none" on rows 10 to 39.
</li>
<ul>
<li>
In Chrome and Firefox, the grid resizes and the remaining rows fill the region. If your browser window is tall enough, the grid will now fit in the space available and will not need to scroll.
</li>
<li>
In Safari (using v14.0.1), the grid appears to resize, but the remaining rows do not properly size (in height) and though the grid may now fit vertically on the display (you'll see all of its outline box fits on the display), you still have to scroll
in the grid to see all the rows -- very odd.
</li>
</ul>
<li>
Next, click "Show Some Rows" to remove the "display: none" class from rows 10 to 39.
</li>
<ul>
<li>
In Chrome and Firefox, the grid rows reappear and the grid resizes back to the way it appeared before.
</li>
<li>
In Safari (using v14.0.1), the rows do not reappear and the grid does not resize.
</li>
<ul>
<li>
UNLESS you resize the Safari window! Resizing the window after clicking "Show Some Rows" appears to force Safari to properly show the rows and resize the grid... Ugh!
</li>
</ul>
</ul>
</ul>
<ul>
<li>
SIDE NOTE 1: In Safari, after you hide the rows, if you click "Hide Grid" (to add a class that sets display: none on the grid element) and then click "show Grid" to remove that class, I find that Safari now does <b>not</b> re-display the grid
-- UNLESS you then resize the Safari window... THEN Safari will re-show the grid AND it will have the content properly sized... Ugh again!
</li>
</ul>
<ul>
<li>
SIDE NOTE 2: In my project, I use this method to qucikly filter complex rows of information in a grid. I encompass each row with an element that has display set to "content" and add a class that sets display to "none" on each row that needs to be hidden
by the filter. This keeps me from having to remove and recreate the content of the row and by wrapping each row in a "display:content" element, I can add the "display:none" class to just that element and don't have to add it to each item in
that row of the grid. BUT, Safari isn't playing nice! I'm hoping this glitch can be fixed before long...
</li>
</ul>
</div>
</div>
</div>
</body>