Chrome/Safari 未填充 flex 父级的 100% 高度

2022-01-30 00:00:00 google-chrome webkit css flexbox

我想要一个具有特定高度的垂直菜单.

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 包含随机数量的子项 (.item),它们总是必须填充父项的高度.为了实现这一点,我使用了 flexbox.

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.

为了使文本与中间对齐,我使用了 display: table-cell 技术.但是使用表格显示需要使用 100% 的高度.

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%.

我的问题是 .item-inner { height: 100% } 在 webkit (Chrome) 中不起作用.

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

  1. 是否有解决此问题的方法?
  2. 或者是否有不同的技术让所有 .item 填充父级的高度,文本垂直对齐到中间?
  1. Is there a fix for this problem?
  2. Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?

这里以jsFiddle为例,应该在Firefox和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;
}

<div class="container">
  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>
</div>

推荐答案

解决方案

使用嵌套的弹性容器.

Solution

Use nested flex containers.

摆脱百分比高度.摆脱表属性.摆脱 vertical-align.避免绝对定位.一直坚持使用 flexbox.

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.

display: flex 应用到弹性项目(.item),使其成为弹性容器.这会自动设置 align-items:stretch,它告诉子级 (.item-inner) 扩展父级的整个高度.

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.

重要提示:从 flex 项中删除指定的高度以使此方法起作用. 如果孩子有指定的高度(例如 height: 100%),那么它将忽略align-items: stretch 来自父级.为了使 stretch 默认工作,孩子的身高必须计算为 auto (full解释).

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).

试试这个(不更改 HTML):

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 */
}

<div class="container">
  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>
</div>

jsFiddle 演示

我的问题是 .item-inner { height: 100% } 不起作用webkit (Chrome).

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 内容高度:height属性

百分比
指定百分比高度.百分比是根据生成框的高度计算的包含块.如果包含块的高度不是明确指定并且此元素不是绝对定位的,该值计算为 auto.

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.

在您的代码中,顶级容器具有定义的高度:.container { height: 20em;}

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

三级容器有一个定义的高度:.item-inner { height: 100%;}

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

但在它们之间,二级容器–.item –没有定义的高度.Webkit 认为这是一个缺失的链接.

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

.item-inner 告诉 Chrome:给我 height: 100%.Chrome 会查看父级 (.item) 以供参考并回应:100% 什么?我什么也没看到(忽略那里的 flex: 1 规则).因此,它根据规范应用 height: auto(内容高度).

.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 现在接受父母的弹性高度作为孩子身高百分比的参考.IE11 和 Edge 也接受弹性高度.

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.

此外,如果与 flex-basis 结合使用,Chrome 将接受 flex-grow 作为适当的父级引用(任何数值都有效)(auto 不会),包括 flex-basis: 0).然而,在撰写本文时,此解决方案在 Safari 中失败.

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;
}

<div id="outer">
  <div id="middle">
    <div id="inner">
      INNER
    </div>
  </div>
</div>

1.指定所有父元素的高度

可靠的跨浏览器解决方案是在所有父元素上指定高度.这可以防止丢失链接,基于 Webkit 的浏览器认为这些链接违反了规范.

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.

请注意,min-heightmax-height 是不可接受的.它必须是 height 属性.

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

更多详情:使用 CSS height 属性和百分比值

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

2.CSS 相关 &绝对定位

position: relative 应用于父级,将 position: absolute 应用于子级.

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

使用 height: 100%width: 100% 调整子节点的大小,或者使用偏移属性:top: 0, >右:0底部:0左:0.

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.删除不必要的 HTML 容器(推荐)

button 周围是否需要两个容器?为什么不删除 .item.item-inner,或两者都删除?虽然 button 元素有时无法作为弹性容器,但它们可以是弹性项目.考虑将 button 设为 .container.item 的子级,并移除无偿标记.

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.

这是一个例子:

.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 */
}

<div class="container">
    <a>Button</a>
    <a>Button</a>
    <a>Button</a>
</div>

4.嵌套 Flex 容器(推荐)

摆脱百分比高度.摆脱表属性.摆脱 vertical-align.避免绝对定位.一直坚持使用 flexbox.

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.

display: flex 应用到弹性项目(.item),使其成为弹性容器.这会自动设置 align-items:stretch,它告诉子级 (.item-inner) 扩展父级的整个高度.

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.

重要提示:从 flex 项中删除指定的高度以使此方法起作用. 如果孩子有指定的高度(例如 height: 100%),那么它将忽略align-items: stretch 来自父级.为了使 stretch 默认工作,孩子的身高必须计算为 auto (full解释).

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).

试试这个(不更改 HTML):

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 */
}

<div class="container">
  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>

  <div class="item">
    <div class="item-inner">
      <a>Button</a>
    </div>
  </div>
</div>

相关文章