7

I generate a table (with vue.js) from an object which is supposed to be two columns wide. Each of the columns comes from the key and value of the object. This is equivalent to the following actual HTML:

<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>

I use CSS grid to format these div into a 2x2 grid, which I expected to be

this is something long on the first row | short 1st row
wazaa 2nd row                           | wazii 2nd row

The code to do that:

#table {
  display: grid;
  grid-template-columns: auto 1fr;
}
<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>

The result is not the one I expect: the two deepest div behave like they should outside of a grid display: they stack on top of each other.

I wanted them to inherit the grid behaviour: to align based on column templates as they go. How to achieve that?

isherwood
  • 58,414
  • 16
  • 114
  • 157
WoJ
  • 27,165
  • 48
  • 180
  • 345
  • 1
    You will have to unwrap the intermediate layer of `
    ` nesting, because that's what CSS grid cannot handle.
    – Terry Feb 05 '19 at 13:42
  • 1
    Yeah, this'll probably require scripting. Why are you undoing the structure that VueJS creates? Why not create the appropriate view in the first place? – isherwood Feb 05 '19 at 13:46
  • You do not create a table you create some divs. Divs have no semantic meaning whereas a table has some meaning. So if you want to display tabular data you should really use a , if you just want to style some unrelated content then use divs with display: grid.
    – cloned Feb 05 '19 at 14:28

3 Answers3

7

You can use display:contents (https://caniuse.com/#feat=css-display-contents) to overcome this:

#table {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-gap:10px;
}

#table > div {
  display:contents;
}
<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>

Or use display table like below:

#table {
  display: table;
}

#table > div {
  display:table-row;
}
#table > div > div {
  display:table-cell;
  padding:5px;
}
#table > div > div:first-child {
  white-space:nowrap;
  width:10%;
}
<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • 2
    @Woj Make sure to read info from caniuse site, because the support is limited. – Chris Tapay Feb 05 '19 at 13:54
  • When using `display: contents` I would suggest a simple JavaScript fallback for browsers that do not support display:contents: https://stackoverflow.com/questions/67033110/check-for-browser-support-of-display-contents – Ben Apr 16 '21 at 08:29
5

Grid properties aren't applying to your content divs because these divs are out-of-scope.

Grid formatting is limited to the parent-child relationship. This means that a grid container is always the parent and a grid item is always the child. Descendants of a grid container beyond the children are not part of grid layout and will not accept grid properties.

Because your content divs are two levels down from the grid container (#table), making them grandchildren not children, they are not grid items and will not accept grid properties.

More details: Grid properties not working on elements inside grid container


There are exceptions to the rule above, but they don't have much or any browser support.

display: contents is covered in another answer to this post. It enables an element to be ignored as a containing block by the parent, so the parent will recognize its grandchildren as normal children. But for now this method is effectively useless for production purposes, as it has weak browser support.

The more appropriate solution in this case would be display: subgrid, which enables the descendants of a grid container beyond the children (i.e., the children of grid items) to respect the lines of the container. But this feature has no browser support.

More details: Positioning content of grid items in primary container (subgrid feature)


If you want a pure CSS solution, maybe a combination of grid and flex can help.

Here's a general concept. No changes to the HTML.

#table {
  display: grid;
  grid-template-columns: 1fr;
}

#table > div {
  display: flex;
}

#table > div > div {
  flex: 1;
}
<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
1

If possible, I would advise to change your Vue.js code to not generate the unneeded nested div level, and make it this:

#table {
  display: grid;
  grid-template-columns: auto 1fr;
}
#table > div { border: 1px dotted black; }
<div id="table">
  <div>
    this is something long on the first row
  </div>
  <div>
    short 1st row
  </div>
  <div>
    wazaa 2nd row
  </div>
  <div>
    wazii 2nd row
  </div>
</div>

If that's not possible then you could use Javascript to achieve the same, but client side. You could also use the display:contents from the answer by @Temani but it has rather limited browser support with possibly buggy results.


If you prefer a Javascript solution instead, you can use this:

(function() {
  var table = document.getElementById("table");
  var divs = [...table.childNodes]; // use ... to enumerate the items immediately
  for (var i = 0; i < divs.length; i++) {
    var div = divs[i];
    while (div.childNodes.length > 0)
      table.appendChild(div.childNodes[0]);
    div.remove();
  }
})()
#table {
  display: grid;
  grid-template-columns: auto 1fr;
}
#table > div { border: 1px dotted black }
<div id="table">
  <div>
    <div>
      this is something long on the first row
    </div>
    <div>
      short 1st row
    </div>
  </div>
  <div>
    <div>
      wazaa 2nd row
    </div>
    <div>
      wazii 2nd row
    </div>
  </div>
</div>
Peter B
  • 22,460
  • 5
  • 32
  • 69