Search code examples
htmlcsslayoutflexboxnested

How to group flexbox items with DIV but without influencing layout, as if DIV was not there?


I have multiple items in flexbox container for which I've achieved desired layout behaviour (considering that .tool-body could be dynamically hidden/shown).

Following html:

<div class="root">
  <div class="toolbar">toolbar1</div>
  <div class="tool-body">body1</div>

  <div class="toolbar">toolbar2</div>
  <div class="tool-body">body2</div>

  <div class="toolbar">toolbar3</div>
  <div class="tool-body">body3</div>
</div>

with css:

.root {
  display: flex;
  flex-direction: column;
  height: 100%;
  width: 100%;
}
.tool-body {
  flex: 1 1 100%;
}

Now, for application logical purposes I would like to wrap those items with <div> to have simpler logic with other aspects of application, such as:

  • keyboard, mouse events (bubbling)

    • $(".my-group").mousemove(...) //...enter/leave
      
  • jQuery DOM traversal use cases

    • $(this).closest(".my-group")... //more chained calls
      
  • css based on nested elements focus

    • .my-group:has(:focus) { /* background/border stuff*/ }
      

So, I would like that html looks like this:

<div class="root">
  <div class="my-group">
    <div class="toolbar">toolbar1</div>
    <div class="tool-body">body1</div>
  </div>

  <div class="my-group">
    <div class="toolbar">toolbar2</div>
    <div class="tool-body">body2</div>
  </div>

  <div class="my-group">
    <div class="toolbar">toolbar3</div>
    <div class="tool-body">body3</div>
  </div>
</div>

...plus extra css for .my-group:

.my-group {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;  /* tried-out many combinations of grow/shrink/basis */
}

But now, layout behaviour is not the same, because container now the .root container has 3 items which are themselves nested flex containers.

I'm aware that flex works by container as parent has some defined direction and direct child elements are items subjected to layout calculations based on their flex-(grow|shrink|basis).

I'm not sure if it even possible to have given html structure but that layout behaves as if <div class="my-group"> was not there at all.

As I said, this extra div wrapping to simplify other aspects, which tend to go super complicated if I can't do that wrapping.

Here is JSFIDDLE which demos my example, side by side:

Given that:

  • there could be 1 up to 4 such .my-groups
  • layout behaviour should be such that
    • when all .tool-body-es are shown, vertical space should be dietributed evenly for each .my-group
    • When some .tool-body-es are hidden, show their corresponding .toolbar and rest of vertical space distribute evenly among others that are shown.

QUESTION(s):

  • How could I achieve layout behaviour of 2nd html to behave as 1st html.
  • is it possible to tell browser following: for layout purposes, pretend that <div class="my-group"> does not exist
  • are there any other workarounds?

Solution

  • To get the browser to ignore .my-group then you could use display: contents on it (see this explainer on css tricks). It basically makes the children of .my-group appear as though they were children of .root. There are some caveats to its use (see caniuse.com) but if you fall outside those limitations then you should be good.

    .root {
      display: flex;
      flex-direction: column;
      height: 100%;
      width: 100%;
    }
    .tool-body {
      flex: 1 1 100%;
    }
    
    .my-group {
      display: contents;
    }
    <div class="root">
      <div class="my-group">
        <div class="toolbar">toolbar1</div>
        <div class="tool-body">body1</div>
      </div>
    
      <div class="my-group">
        <div class="toolbar">toolbar2</div>
        <div class="tool-body">body2</div>
      </div>
    
      <div class="my-group">
        <div class="toolbar">toolbar3</div>
        <div class="tool-body">body3</div>
      </div>
    </div>