0

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>
halfer
  • 19,824
  • 17
  • 99
  • 186
FTLPhysicsGuy
  • 1,035
  • 1
  • 11
  • 23
  • 1
    Just to confirm your observations - it also works OK for me on Edge Windows 10. With Safari on iPadIOS 14 it misbehaves in the way you describe. It even did the recovery on pinch zooming the window on the iPad. – A Haworth Nov 14 '20 at 21:15
  • Thanks, for the additional info, @A Haworth – FTLPhysicsGuy Nov 14 '20 at 21:20
  • Side note: I've tried finding a way to force Safari to "recalculate" the grid and child sizes after applying the filtering (whatever it's doing when you resize the window), but I haven't had much luck. – FTLPhysicsGuy Nov 15 '20 at 03:17

1 Answers1

1

If we give the main-div an explicit height - say 500px - then Safari (at least on iPadIOS 14.2) behaves as expected - rows can be removed from the grid and are put back as on Chrome/Firefox/Edge (at least on Windows 10).

The containing div already has a height (100vh) through the .body class but that does not seem to be enough. Indeed, giving main-div a height of 100% does not solve the problem, and, even more strangely, giving it a height of 100vh does not either.

I cannot explain this behaviour, though it may have something to do with the way things like % height are defined (or not - see for example a discussion at CSS Grid Row Height Safari Bug

Setting a px height is unlikely to be an acceptable workaround I realise, but it seemed worth putting the info here in case it helps find to another way round the problem. Also there may be a tie-up with the fact that the snippet in the question works in non-full-screen mode - though there shouldn't be an affect of being in an iframe in a constrained area there does seem to be.

Here's the snippet with height 500px:

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;
  height: 500px;
}

.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>
A Haworth
  • 30,908
  • 4
  • 11
  • 14
  • Thanks for checking it out. I might be able to come up with a general method for a work around by forcing a size if that's what it takes. – FTLPhysicsGuy Nov 15 '20 at 03:00