Alexander Farber
Alexander Farber

Reputation: 22988

Nested CSS flexbox problem - only with Safari 14 / MacOS browser

I have prepared a simple test case (below), which uses a nested CSS flexbox to give max possible space to the checkered #pixiCanvas, while keeping the yellow #hintDiv and #totalDiv at the screen top/bottom:

Firefox

The problem is that while my code works perfectly on other browsers (Firefox, Chrome, Opera, Edge, even IE 11; after I have followed the advice to add min-width, min-height to #mainDiv and its children) it does not work with Safari 14 / MacOS -

The #mainDiv or maybe #gameDiv grow too tall and push #totalDiv out of the screen, while #pixiCanvas is cut off and not displayed entirely:

Safari 14

The structure of my web page is:

- fullDiv (should occupy 100% / 100 vh)
-- leftDiv
-- mainDiv
--- hintDiv (100% width, on top of screen)
--- gameDiv (should have max possible width and height)
--- totalDiv (100% width, on bottom of screen)
-- rightDiv

Here is my code (open in Safari to see the issue):

  $(function() {
    $(':button').button();
    $('#fullCheck').checkboxradio();

    var gamesMenu = $('#gamesMenu').menu({
      items: '> :not(.ui-widget-header)',
      select: function(ev, ui) {
        ui.item.addClass('selected').siblings().removeClass('selected');
      }
    });

    for (var game = 1; game <= 50; game++) {
      gamesMenu.append('<LI CLASS="ui-menu-item-wrapper" VALUE="' + game + '">GAME ' + game + '</LI>');
    }

    gamesMenu.menu('refresh');

    var WIDTH = 400;
    var HEIGHT = 400;

    var app = new PIXI.Application({
      width: WIDTH,
      height: HEIGHT,
      view: document.getElementById('pixiCanvas'),
      backgroundColor: 0xFFFFFF
    });

    var background = new PIXI.Graphics();
    for (var i = 0; i < 8; i++) {
      for (var j = 0; j < 8; j++) {
        if ((i + j) % 2 == 0) {
          background.beginFill(0xCCCCFF);
          background.drawRect(i * WIDTH / 8, j * HEIGHT / 8, WIDTH / 8, HEIGHT / 8);
          background.endFill();
        }
      }
    }
    app.stage.addChild(background);
  });
body {
  margin: 0;
  padding: 0;
}

#fullDiv {
  width: 100%;
  height: 100vh;
  background: #FFF;
  display: flex;
}

#hintDiv,
#totalDiv {
  font-style: italic;
  text-align: center;
  background: yellow;
}

#leftDiv {
  text-align: center;
  background: #FCC;
  display: flex;
  flex-direction: column;
}

#gamesMenu {
  overflow: auto;
}

#mainDiv {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 200px;
  min-height: 200px;
  
  box-sizing: border-box;
  border: 1px green dashed;
}

#gameDiv {
  flex-grow: 1;
  min-width: 150px;
  min-height: 150px;
}

#pixiCanvas {
  width: 100%;
  height: 100%;
  min-width: 100px;
  min-height: 100px;
  background: #CCF;

  box-sizing: border-box;
  border: 4px red dotted;
}

#rightDiv {
  text-align: center;
  background: #CFC;
  overflow-y: auto;
}

li.selected {
  font-weight: bold;
  background-color: yellow;
}
<div id="fullDiv">
  <div id="leftDiv">
    <button id="newBtn">New game</button>
    <ul id="gamesMenu"></ul>
  </div>

  <div id="mainDiv">
    <div id="hintDiv">Hint</div>

    <div id="gameDiv">
      <canvas id="pixiCanvas"></canvas>
    </div>

    <div id="totalDiv">Total score</div>
  </div>

  <div id="rightDiv">
    <input id="fullCheck" type="checkbox">
    <label for="fullCheck">Full screen</label><br>
    <button id="recallBtn">Recall</button><br>
    <button id="shuffleBtn">Shuffle</button><br>
    <button id="swapBtn">Swap</button><br>
    <button id="skipBtn">Skip</button><br>
    <button id="resignBtn">Resign</button><br>
    <button id="pileBtn">Pile</button><br>
    <button id="movesBtn">Moves history</button><br>
    <button id="shareBtn">Share</button><br>
    <button id="playBtn">Play</button>
  </div>
</div>

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> 
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pixi.min.js"></script>

Here the screenshot of my word game, where the #pixiCanvas grows too large and is shown underneath the grey chat area at the bottom:

Safari

The only workaround I have been able to come up sofar has been:

if (navigator.userAgent.indexOf('Safari') >= 0) {
    $('#gameDiv').css('overflow', 'auto');
}

but it makes the usage of my word game awkward while dragging letters.

P.S. I would prefer not to switch to CSS grid, because I have an impression that CSS flex is better supported (and some of my users still use IE 11).

Upvotes: 1

Views: 1610

Answers (1)

MaxiGui
MaxiGui

Reputation: 6348

Simply add display: contents; to #gameDiv:

#gameDiv{
  display: contents;
}

Browser compatibility for display contents

As you can see this display is not compatible with IE. But this is fine because IE will ignore it and apply default display: block that works fine in IE (I tested it).

Any if you want it to be sure to apply the right display, you can still keep display: block by using 2 display as in this subject.

Using:

@supports (display: contents) {
  #gameDiv { display: contents; }
}

Demo:

$(function() {
    $(':button').button();
    $('#fullCheck').checkboxradio();

    var gamesMenu = $('#gamesMenu').menu({
      items: '> :not(.ui-widget-header)',
      select: function(ev, ui) {
        ui.item.addClass('selected').siblings().removeClass('selected');
      }
    });

    for (var game = 1; game <= 50; game++) {
      gamesMenu.append('<LI CLASS="ui-menu-item-wrapper" VALUE="' + game + '">GAME ' + game + '</LI>');
    }

    gamesMenu.menu('refresh');

    var WIDTH = 400;
    var HEIGHT = 400;

    var app = new PIXI.Application({
      width: WIDTH,
      height: HEIGHT,
      view: document.getElementById('pixiCanvas'),
      backgroundColor: 0xFFFFFF
    });

    var background = new PIXI.Graphics();
    for (var i = 0; i < 8; i++) {
      for (var j = 0; j < 8; j++) {
        if ((i + j) % 2 == 0) {
          background.beginFill(0xCCCCFF);
          background.drawRect(i * WIDTH / 8, j * HEIGHT / 8, WIDTH / 8, HEIGHT / 8);
          background.endFill();
        }
      }
    }
    app.stage.addChild(background);
  });
body {
  margin: 0;
  padding: 0;
}

#fullDiv {
  width: 100%;
  height: 100vh;
  background: #FFF;
  display: flex;
}

#hintDiv,
#totalDiv {
  font-style: italic;
  text-align: center;
  background: yellow;
}

#leftDiv {
  text-align: center;
  background: #FCC;
  display: flex;
  flex-direction: column;
}

#gamesMenu {
  overflow: auto;
}

#mainDiv {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-width: 200px;
  min-height: 200px;
  
  box-sizing: border-box;
  border: 1px green dashed;
}

#gameDiv {
  flex-grow: 1;
  min-width: 150px;
  min-height: 150px;
}

#pixiCanvas {
  width: 100%;
  height: 100%;
  min-width: 100px;
  min-height: 100px;
  background: #CCF;

  box-sizing: border-box;
  border: 4px red dotted;
}

#rightDiv {
  text-align: center;
  background: #CFC;
  overflow-y: auto;
}

li.selected {
  font-weight: bold;
  background-color: yellow;
}

#gameDiv{
  display: contents;
}
<div id="fullDiv">
  <div id="leftDiv">
    <button id="newBtn">New game</button>
    <ul id="gamesMenu"></ul>
  </div>

  <div id="mainDiv">
    <div id="hintDiv">Hint</div>

    <div id="gameDiv">
      <canvas id="pixiCanvas"></canvas>
    </div>

    <div id="totalDiv">Total score</div>
  </div>

  <div id="rightDiv">
    <input id="fullCheck" type="checkbox">
    <label for="fullCheck">Full screen</label><br>
    <button id="recallBtn">Recall</button><br>
    <button id="shuffleBtn">Shuffle</button><br>
    <button id="swapBtn">Swap</button><br>
    <button id="skipBtn">Skip</button><br>
    <button id="resignBtn">Resign</button><br>
    <button id="pileBtn">Pile</button><br>
    <button id="movesBtn">Moves history</button><br>
    <button id="shareBtn">Share</button><br>
    <button id="playBtn">Play</button>
  </div>
</div>

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> 
<script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pixi.min.js"></script>

Upvotes: 2

Related Questions