Chrome/Safari无法填充flex容器的100%高度

303

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

每个子元素都必须填充父元素的高度,并具有垂直居中的文本。

子元素的数量是随机的,因此我必须使用动态值来解决问题。

.container div 包含随机数量的子元素(.item),它们总是要填充父元素的高度。为了实现这一点,我使用了 flexbox。

为了使文本与链接垂直居中,我使用了 display: table-cell 技术。但是使用表格显示需要使用高度为 100%。

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

  1. 是否有解决此问题的方法?
  2. 或者是否有其他技术可以使所有的 .item 填充父元素的高度并使文本垂直居中?

在 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>


1
现在已经修复 - https://bugs.chromium.org/p/chromium/issues/detail?id=426898 - vsync
4
这段内容与 OP 的问题无直接关联,但对于搜索“safari display absolute height 100% doesnt work”或类似问题的人可能有帮助。我在 flex 容器内有一个类似这样的元素,必须指定 top:0left:0 才能按预期显示。 - AlexMA
1
@vsync还没有修复:https://dev59.com/1aXja4cB1Zd3GeqPNS0n - TylerH
5个回答

506

解决方案

使用嵌套的弹性容器。

摆脱百分比高度。摆脱表格属性。摆脱vertical-align。避免绝对定位。只需完全使用 flexbox。

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

重要提示:为了使此方法起作用,请从弹性项目中删除指定的高度。如果子元素指定了高度(例如height: 100%),则它将忽略来自父元素的align-items: stretch。为了使默认的stretch起作用,子元素的高度必须计算为auto(完整说明)

尝试这个(不更改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 demo


解释

我的问题是,.item-inner { height: 100% } 在webkit(Chrome)中无法工作。

它无法工作,因为您正在以不符合规范传统实现的方式使用百分比高度。

10.5 内容高度: height 属性

百分比
指定一个百分比高度。百分比是相对于生成的框的包含块的高度计算的。如果未明确指定包含块的高度并且此元素未绝对定位,则该值计算为auto

自动
高度取决于其他属性的值。

换句话说,要使百分比高度在流动子级上起作用,父级必须具有设置的高度。

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

第三级容器已定义高度:.item-inner { height: 100%; } 但在它们之间,第二级容器 - .item - 没有定义高度。 Webkit 将其视为缺失的链接。 .item-inner 告诉 Chrome:给我 height: 100%。Chrome 寻找父元素 (.item) 作为参考,并回应:100% 是相对于什么?我没有看到任何东西(忽略了存在的 flex: 1 规则)。因此,它按照规范应用 height: auto (内容高度)。
另一方面,Firefox 现在接受父元素的 flex 高度作为子元素百分比高度的参考。IE11 和 Edge 也接受 flex 高度。
此外,如果与flex-basis一起使用(任何数字值都可以,auto不行,包括flex-basis:0),Chrome将接受flex-grow作为足够的父级参考。然而,截至本文撰写时,这种解决方案在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 浏览器则认为这是违反规范的。

请注意,min-heightmax-height 不可接受。必须使用 height 属性。

更多细节请参见:使用 CSS 的 height 属性和百分比值

2. CSS 相对和绝对定位

position: relative 应用于父元素,将 position: absolute 应用于子元素。

使用 height: 100%width: 100% 来设置子元素的大小,或使用偏移属性:top: 0right: 0bottom: 0left: 0

使用绝对定位时,父元素没有指定高度也可以使用百分比高度。

3. 删除不必要的HTML容器(建议)

是否需要在button周围放置两个容器?为什么不删除.item.item-inner,或者两者都删除?尽管button元素有时作为弹性容器失败, 它们可以作为弹性项目。考虑将button作为.container.item的子级,并删除多余的标记。

这是一个例子:

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

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

重要提示:删除此方法中 flex 项的指定高度。 如果子元素有指定的高度 (例如,height: 100%),那么它将忽略来自父元素的 align-items: stretch。要使默认的 stretch 生效,子元素的高度必须计算为 auto (完整解释)

尝试这样做(不更改 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


4
我使用height:auto解决了我的问题。 - Alfrex92
3
我可以确认,使用嵌套的 flex 容器效果最佳。我们在菜单中不一致地使用了 flexbox。有些地方我们使用了高度为 100% 而不是在父容器上使用 flex:1 和 display:flex。在我们替换这些对应的定义后,一切都按预期运行了。感谢 @Michael_B 给出的提示! :) - flocbit
2
@JamesHarrington,CSS随着时间的推移变得越来越复杂;-) - DerMike
1
我发誓,你是一个完全的救星。我从未这样看待过它:“摆脱一切,全程使用flexbox”,这使我的样式在任何情况下更加一致。 - C.Tale
1
这是一个混乱的实现。Flexbox 应该用于指定的元素,而不是弄乱整个结构,或者说是父级结构。但还是谢谢你的回答。 - LuckyLuke Skywalker
显示剩余6条评论

40

在容器中指定flex属性对我很有用:

.container {
    flex: 0 0 auto;
}

这确保了高度已设置,并且不会继续增长。


我非常希望这能够工作,但最终容器却在视口底部。 - boatcoder

16

谢谢,这对我很有帮助。Flex子元素会自动拉伸到其容器的高度吗?这就是为什么它有效的原因吗? - Reece Kenney
@ReeceKenney,是的,回答了你的问题。请看我的答案,解决方案#4。 - Michael Benjamin

11
对于移动版 Safari 浏览器,有一个浏览器修复方案。您需要为 iOS 设备添加 -webkit-box
例如:
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;

如果您在父元素上使用align-items: stretch;属性,请从子元素中删除height : 100%


哇,非常感谢你,你救了我很多麻烦!display: -webkit-box; 正是我所需要的。结果在2020年末,iOS设备仍然没有可预测的flex支持。 - Big Money

2
我在iOS 8、9和10中遇到了类似的问题,上面的信息无法解决它,但是经过一天的努力,我找到了一个解决方案。尽管这个方法不适用于所有人,但在我的情况下,我的项目堆叠在一列中,高度为0,而实际应该是内容高度。将CSS改为行并换行可以解决这个问题。这仅适用于单个项目且它们被堆叠,但由于我花了一天的时间才发现这一点,我认为我应该分享我的修复方法!
.wrapper {
    flex-direction: column; // <-- Remove this line
    flex-direction: row; // <-- replace it with
    flex-wrap: wrap; // <-- Add wrapping
}

.item {
    width: 100%;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接