ChatGPT解决这个技术问题 Extra ChatGPT

Chrome / Safari not filling 100% height of flex parent

I want to have a vertical menu with a specific height.

Each child must fill the height of the parent and have middle-aligned text.

The number of children is random, so I have to work with dynamic values.

Div .container contains a random number of children (.item) that always have to fill the height of the parent. To achieve that I used flexbox.

For making links with text aligned to the middle I am using display: table-cell technique. But using table displays requires using a height 100%.

My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).

Is there a fix for this problem? Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?

Example here jsFiddle, should be viewed in Firefox and Chrome

.container { height: 20em; display: flex; flex-direction: column; border: 5px solid black } .item { flex: 1; border-bottom: 1px solid white; } .item-inner { height: 100%; width: 100%; display: table; } a { background: orange; display: table-cell; vertical-align: middle; }

This isn't specifically relevant to OP, but might be relevant for googlers who land here for "safari display absolute height 100% doesnt work" or something similar. I have an element like this inside a flex container and had to specify top:0 and left:0 to have it appear as expected.
@vsync hasn't been fixed yet: stackoverflow.com/questions/46226298/…

M
Michael Benjamin

Solution

Use nested flex containers.

Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.

Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.

Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).

Try this (no changes to HTML):

.container { display: flex; flex-direction: column; height: 20em; border: 5px solid black } .item { display: flex; /* new; nested flex container */ flex: 1; border-bottom: 1px solid white; } .item-inner { display: flex; /* new; nested flex container */ flex: 1; /* new */ /* height: 100%; <-- remove; unnecessary */ /* width: 100%; <-- remove; unnecessary */ /* display: table; <-- remove; unnecessary */ } a { display: flex; /* new; nested flex container */ flex: 1; /* new */ align-items: center; /* new; vertically center text */ background: orange; /* display: table-cell; <-- remove; unnecessary */ /* vertical-align: middle; <-- remove; unnecessary */ }

jsFiddle demo

Explanation

My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).

It's not working because you're using percentage height in a way that doesn't conform with the traditional implementation of the spec.

10.5 Content height: the height property percentage 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 and this element is not absolutely positioned, the value computes to auto. auto The height depends on the values of other properties.

In other words, for percentage height to work on an in-flow child, the parent must have a set height.

In your code, the top-level container has a defined height: .container { height: 20em; }

The third-level container has a defined height: .item-inner { height: 100%; }

But between them, the second-level container – .itemdoes not have a defined height. Webkit sees that as a missing link.

.item-inner is telling Chrome: give me height: 100%. Chrome looks to the parent (.item) for reference and responds: 100% of what? I don't see anything (ignoring the flex: 1 rule that is there). As a result, it applies height: auto (content height), in accordance with the spec.

Firefox, on the other hand, now accepts a parent's flex height as a reference for the child's percentage height. IE11 and Edge accept flex heights, as well.

Also, Chrome will accept flex-grow as an adequate parent reference if used in conjunction with flex-basis (any numerical value works (auto won't), including flex-basis: 0). As of this writing, however, this solution fails in Safari.

#outer { display: flex; flex-direction: column; height: 300px; background-color: white; border: 1px solid red; } #middle { flex-grow: 1; flex-basis: 1px; background-color: yellow; } #inner { height: 100%; background-color: lightgreen; }

INNER

Four Solutions

1. Specify a height on all parent elements

A reliable cross-browser solution is to specify a height on all parent elements. This prevents missing links, which Webkit-based browsers consider a violation of the spec.

Note that min-height and max-height are not acceptable. It must be the height property.

More details here: Working with the CSS height property and percentage values

2. CSS Relative & Absolute Positioning

Apply position: relative to the parent and position: absolute to the child.

Size the child with height: 100% and width: 100%, or use the offset properties: top: 0, right: 0, bottom: 0, left: 0.

With absolute positioning, percentage height works without a specified height on the parent.

3. Remove unnecessary HTML containers (recommended)

Is there a need for two containers around button? Why not remove .item or .item-inner, or both? Although button elements sometimes fail as flex containers, they can be flex items. Consider making button a child of .container or .item, and removing gratuitous mark-up.

Here's an example:

.container { height: 20em; display: flex; flex-direction: column; border: 5px solid black } a { flex: 1; background: orange; border-bottom: 1px solid white; display: flex; /* nested flex container (for aligning text) */ align-items: center; /* center text vertically */ justify-content: center; /* center text horizontally */ }

4. Nested Flex Containers (recommended)

Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.

Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.

Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).

Try this (no changes to HTML):

.container { display: flex; flex-direction: column; height: 20em; border: 5px solid black } .item { display: flex; /* new; nested flex container */ flex: 1; border-bottom: 1px solid white; } .item-inner { display: flex; /* new; nested flex container */ flex: 1; /* new */ /* height: 100%; <-- remove; unnecessary */ /* width: 100%; <-- remove; unnecessary */ /* display: table; <-- remove; unnecessary */ } a { display: flex; /* new; nested flex container */ flex: 1; /* new */ align-items: center; /* new; vertically center text */ background: orange; /* display: table-cell; <-- remove; unnecessary */ /* vertical-align: middle; <-- remove; unnecessary */ }

jsFiddle


I fixed my issue using height:auto
just some remark taken from the explanation link of this great post - "align-items: stretch" makes flex children to expand across the cross axis of the container - that is to stretch children to the height, the flex container should have flex-direction as row.
I can confirm that using nested flex containers works best. We used flexbox inconsistently in our menu. Instead of using flex: 1 and display: flex on parent containers we used height: 100% at some places. Everything worked as expected after we replaced the corresponding definitions. Thanks @Michael_B for the hint! :)
@JamesHarrington, CSS has grown over time and is complex ;-)
Each solution should be a different answer. How can I vote in a specific solution this way?
h
huseyin39

Specifying a flex attribute to the container worked for me:

.container {
    flex: 0 0 auto;
}

This ensures the height is set and doesn't grow either.


Fixed it for me! What a lifesaver.
I had so wanted this to work, but I end up with the container off the bottom of the viewport
work for me. thank you so much. before that don't understand why my height is only 1px on safari when redirect to another page.
n
nghiepit

Solution: Remove height: 100% in .item-inner and add display: flex in .item

Demo: https://codepen.io/tronghiep92/pen/NvzVoo


Thanks, this helped me. Do flex children automatically take stretch to the height of their container? Is this why this works?
@ReeceKenney, yes, to your questions. This is explained in my answer. See solution #4.
N
Narasinghe

For Mobile Safari There is a Browser fix. you need to add -webkit-box for iOS devices.

Ex.

display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;

if you're using align-items: stretch; property for parent element, remove the height : 100% from the child element.


omg thank you so much you saved me from so much headache! display: -webkit-box; is exactly what I needed. It turns out even in late 2020, iOS devices still don't have predictable flex support
D
Designer023

I have had a similar issue in iOS 8, 9 and 10 and the info above couldn't fix it, however I did discover a solution after a day of working on this. Granted it won't work for everyone but in my case my items were stacked in a column and had 0 height when it should have been content height. Switching the css to be row and wrap fixed the issue. This only works if you have a single item and they are stacked but since it took me a day to find this out I thought I should share my fix!

.wrapper {
    flex-direction: column; // <-- Remove this line
    flex-direction: row; // <-- replace it with
    flex-wrap: wrap; // <-- Add wrapping
}

.item {
    width: 100%;
}