Why is my Grid element's height not being calculated correctly?

依然范特西╮ 提交于 2019-11-26 17:33:57

问题


I'm having trouble with a CSS grid element's height. The code I am using is:

.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  width: 100px;
  height: 100px;
  grid-template-areas: 'windowContentHolder';
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="gridContainer">
  <div class="gridItem">
    <div class="content">hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>
    </div>
  </div>
</div>

As you can see the gridItem is set to be height:200% and the expected result is not as intended. It should be twice as high (200px) as the parent (100px), with any extra height being hidden by the scroll bar, instead though the height property doesn't seem to be setting at all.

It's seems like the percentage is considering the child height instead of the parent height because if we inspect closely the element we will see that its height is twice the height of the child element.

The element with 'hi' is not overflowing as would be expected. Changing the gridContainer to 'block' does work as expected, but not with 'grid':

.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: block;
  width: 100px;
  height: 100px;
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
  overflow: auto;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="gridContainer">
  <div class="gridItem">
    <div class="content">hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>
    </div>
  </div>
</div>

回答1:


The height of the grid container is fixed at 100px.

The height of the grid item is set to 200%.

A grid item exists inside tracks, which exist inside the container.

You can think of grid items as existing two levels down from the container.

Put another way, the parent of the grid item is the track, not the container.

Since your row height is neither fixed or a true unit of length – it's set to 1fr – the grid item's percentage height fails and it is free to expand the row as necessary (height: auto).

Whatever fixed height you set on the container, set it on the row, as well.

.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 100px;    /* adjustment; value was 1fr */
  width: 100px;
  height: 100px;
  grid-template-areas: 'windowContentHolder';
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
  overflow: auto;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="gridContainer">
  <div class="gridItem">
    <div class="content">hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>
    </div>
  </div>
</div>



回答2:


First, let's start by defining how percentage height works:

Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'.ref

There is also another part of the specification dealing with percentage values and cycles:

Sometimes the size of a percentage-sized box’s containing block depends on the intrinsic size contribution of the box itself, creating a cyclic dependency. When calculating the intrinsic size contribution of such a box , a cyclic percentage—that is, a percentage value that would resolve against a containing block size which itself depends on that percentage—is resolved specially ... (then a lot of rules)

Basically, in some particular cases, the browser is able to resolve percentage value even if the containing block height is not specified explicitely.


As Michael_B said the element exists inside tracks that are inside the container so our containing block here is no more the container but the track.

Grid track is a generic term for a grid column or grid row—in other words, it is the space between two adjacent grid lines. Each grid track is assigned a sizing function, which controls how wide or tall the column or row may grow, and thus how far apart its bounding grid lines are. Adjacent grid tracks can be separated by gutters but are otherwise packed tightly.ref

Now how these elements are sized?

The tracks (rows and columns) of the grid are declared and sized either explicitly through the explicit grid properties or are implicitly created when items are placed outside the explicit grid. The grid shorthand and its sub-properties define the parameters of the gridref

We can also read more about this here: 6.2. Grid Item Sizing, here: 6.6. Automatic Minimum Size of Grid Items and also here 7.2. Explicit Track Sizing


Well, all these specifications are somehow difficult to follow but here is my own interpretation to understand what is happening.

The size of the tracks is first calculated by the browser considering content and grid properties and then this height is used as reference for the percentage value.

Here is another example to better show what is happening:

.box {
  display: grid;
  height: 100px;
  width: 200px;
  grid-template-columns: 1fr 1fr;
  grid-gap:5px;
  background: red;
}

.box>div {
  background: rgba(0, 0, 0, 0.4);
  height: 200%;
}
<div class="box">
  <div class="first">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
  <div class="second">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
</div>

We have a container with display:grid and 2 columns, the first column contain more content than the second one and we applied to both height:200% and surprisingly both have the height which is twice the content height of the first one!

If we check the devtools we can see this:

The dotted boxes define our track size where inside we have two grid cells. Since it's a grid the height of the track will be defined by the tallest content which will also make both cells to have that same height. So height:200% is not exactly the height of the content but the height of the track that was initially defined by the content.

If we check again the answer of Micheal_B, explicitely defining the track height will also give us a logical and trivial result since the calculation is easy and we don't have a complex cycle.

.box {
  display: grid;
  height: 100px;/*this will not be considered*/
  width: 200px;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 150px;/* this will be considered*/
  grid-gap: 5px;
  background: red;
}

.box>div {
  background: rgba(0, 0, 0, 0.4);
  height: 200%;
}
<div class="box">
  <div class="first">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
  <div class="second">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
</div>

As we can see, I specified the track to be 150px thus height:200% is equal to 300px.


This is not the only case. I also found another case with flexbox where we can use percentage height without any explicit height on the containing block.

.container {
  height:200px;
  background:red;
  display:flex;
}
.box {
  width:100px;
  background:blue;
}

.box > div {
  background:yellow;
  height:50%;
}
<div class="container">
  <div class="box">
    <div></div>
  </div>
</div>

As we can see the height:50% is working fine making the yellow box to be 50% of its parent element (the blue box).

My own explication is related to the default stretch behavior of flexbox. By default, a flex item doesn't have a height defined by content but its height is stretched to fill the parent container height so the browser will calculate a new height which make the percentage value of the child to be relatively to this height.

Here is another example that shows a similar behavior of the grid example:

.box {
  display: flex;
  width: 200px;
  background: red;
}

.box>div {
  background: rgba(0, 0, 0, 0.4);
}
.second >div {
  height:200%;
  background:yellow;
  width:50px;
}
<div class="box">
  <div class="first">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
  <div class="second">
    <div></div>
  </div>
</div>

The height of the parent is defined by the tallest element, the second element is stretched to that height and the height of yellow element is twice that same height.

In other words, what we have is somehow logical because even if we didn't specify an explicit height, the browser is able to first calculate the height of the containing block then resolve the percentage using the calculated value.

UPDATE

Here is another intresting result consider the top property. We also know that percentage value of top also refers to the height of the containing block of the element and this height need to be defined.

Here is an illustration:

.box {
  border:5px solid;
}
.box > div {
  position:relative;
  top:100%; 
  height:100px;
  background:red;
}
<div class="box">
  <div>This div won't move because the parent has no height specified</div>
</div>

<div class="box" style="height:100px;">
  <div>This div will move because the parent has a height specified</div>
</div>

Now if we consider our previous cases, top will work with percentage values like height is doing.

With CSS-grid

.box {
  display: grid;
  height: 100px;/*this will not be considered*/
  width: 200px;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 150px;/* this will be considered*/
  grid-gap: 5px;
  background: red;
}

.box>div {
  position:relative;
  top:100%;
  background: rgba(0, 0, 0, 0.4);
  height: 200%;
}
<div class="box">
  <div class="first">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
  <div class="second">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
</div>
<div class="b">
  <div class="a">
  
  </div>
</div>

With flexbox:

.box {
  display: flex;
  width: 200px;
  background: red;
}

.box>div {
  background: rgba(0, 0, 0, 0.4);
}
.second >div {
  position:relative;
  top:100%;
  height:200%;
  background:yellow;
  width:50px;
}
<div class="box">
  <div class="first">
    lorem<br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br> lorem
    <br>
  </div>
  <div class="second">
    <div></div>
  </div>
</div>



回答3:


As BoltClock♦ said in the comment thread that the asker is interested in understanding the behavior, the best explanation according to me could be as follows:

As per the question => Why / How is the height being calculated from the child?

Here the height isn't being calculated from the child. If we refer below:

From the spec:

5.2. Sizing Grid Containers

You would get to know that the div having the class ".gridContainer " acts as the parent wrapper for the whole snippet and it has display:grid applied on it. When that is the case, the sizing of the parent will be decided by its max-content size.

Once the size has been set, the div with class ".gridItem" will take up the width and height according to its parent. Therefore it seems like the height has been calculated from the child although that's not the case. This behavior could be best explained from the below scenarios:

1)

.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  width: 100px;
  height: 100px;
  grid-template-areas: 'windowContentHolder';
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
  overflow: auto;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="gridContainer">
  <div class="gridItem">
    <div class="content">hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi hi 
    </div>
  </div>
</div>

2)

.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 1fr;
  width: 100px;
  height: 100px;
  grid-template-areas: 'windowContentHolder';
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
  overflow: auto;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="gridContainer">
  <div class="gridItem">
    <div class="content">hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>
    </div>
  </div>
</div>

So, it can be inferred from the above two scenarios, the Grid sizing is being dictated by its content.




回答4:


.wrap {
  height: 100px;
  width: 100px;
}
.gridContainer {
  border: thin solid black;
  background: rgba(255, 0, 0, 0.5);
  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 100%;
  width: 100%;
  height: 100%;
  grid-template-areas: 'windowContentHolder';
}

.gridItem {
  grid-area: windowContentHolder;
  background: rgba(255, 255, 0, 0.5);
  width: 200%;
  height: 200%;
  overflow: auto;
}

.content {
  background: rgba(255, 0, 0, 0.5);
}
<div class="wrap">
 <div class="gridContainer">
  <div class="gridItem">
   <div class="content">hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>hi<br/>
   </div>
  </div>
 </div>
</div>

You could try setting the grid-template-rows to be 100%. This would allow your gridItem to occupy 200% of the gridContainer

This means the parent of your gridContainer element must be a block element and have a specified height value.

Reading from the grid-template-rows definition on MDN

(percentage) Is a non-negative value, relative to the block size of the grid container.

Try wrapping your gridContainer in another div and setting the height: 100px; on that instead. Then you can use a percentage value here grid-template-rows: 100%;



来源:https://stackoverflow.com/questions/51962136/why-is-my-grid-elements-height-not-being-calculated-correctly

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!