Flex-Basis: 0%在IE11中会导致错误的溢出

8
我遇到的问题涉及flex-basis:0%以及IE11在用户缩小窗口宽度时如何处理它。这可能与box-sizing的错误有关。
我有一些数量不定的flex子元素,每个子元素的宽度都不确定。每个子元素都有任意动态生成的内容。但是,其中一些内容必须具有文本换行的能力。
flex容器本身必须具有100%的宽度,并且必须能够包裹其子元素(即flex-wrap:wrap)。
假设存在三个flex子元素,最后一个需要文本换行:
<div class="flex-container">
  <div class="flex-child">
    <div>This text should not overlay other flex-children.</div>
  </div>
  <div class="flex-child">
    <div>This text should not overlay other flex-children.</div>
  </div>
  <div class="flex-child">
    <div class="text-break">This text should break on new lines when the container shrinks down</div>
  </div>
</div>

CSS可以定义如下:
.flex-container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}

.flex-child {
  flex-grow: 1;
  flex-shrink: 0;
  flex-basis: 0%;
  white-space: nowrap;
  padding: 5px;
}

.text-break {
  white-space: pre-wrap;
}

当窗口足够宽时,每个flex-child应该并排显示,文本不会折叠: 正确展示-最大宽度窗口 当窗口水平缩小时,右侧框中的文本应开始断行: 正确展示-中等宽度窗口 当文本无法再断成更多行时,flex-boxes本身应开始换行: 正确展示-最小宽度窗口 这在大多数现代浏览器中都很好用,但是在IE11上却不行。在IE11中,当改变屏幕尺寸时,文本可以正确换行,但flex子元素却无法换行。由于flex子元素无法换行,它们的内容会溢出到彼此之间: Incorrect IE11 Display - flex-basis: 0% 将flex基础设置为flex-basis: auto;则产生相反的效果:文本不会换行,但flex子元素会换行,而且没有内容溢出(在此截图中,绿色框中的文本应该断行,而不是flex-box换行): Incorrect IE11 Display - flex-basis: auto 我看到的大多数解决方案需要固定长度的 flex-children,但我无法使用它们,因为它们是动态生成的。我故意没有使用 flex 快捷属性,因为它与其他不相关的问题有关。这个答案 建议使用 _:-ms-fullscreen, :root .IE11-only-class { /* IE11 特定属性 */ },如果我能解决换行问题,可能会起作用,但我无法解决这个问题。
我还应该说,我只需要在最新的浏览器上工作(技术上只需要在某些版本的 Chrome 和 IE11 上工作,但其他浏览器和版本也能工作是一个加分项)。
这里有一个 代码片段 全面展示了问题(在 IE11 和 Chrome 中查看以查看差异)。
有人有什么想法来解决这个问题吗?
记住,要求如下:
  • 未知数量的宽度未指定的 flex 子元素
  • 某些文本必须在 flex 盒子换行之前换行
  • 一旦文本无法再换行,flex 子元素必须换行
  • Chrome(我使用的是 59.0.3071.109)和 IE11(我使用的是 11.0.9600.18816CO)应该以相同的方式运行。
  • 首选 CSS-only 解决方案

谢谢。

更新:

我的一个同事建议为不包含可换行文本的 flex 子元素使用单独的类。以下是他使用的 HTML 和 CSS:

<!--HTML-->
<div class="flex-container">
  <div class="flex-child child-1">
    <div>This text should not overlay other flex-children.</div>
  </div>
  <div class="flex-child flex-child-without-textwrap child-2">
    <div>This text should not overlay other flex-children.</div>
  </div>
  <div class="flex-child flex-child-with-textwrap child-3">
    <div class="text-break">This text should break on new lines when the container shrinks down</div>
  </div>
</div>

/*CSS*/
.flex-container {
  display: flex;
  flex-wrap: wrap;
}

.flex-child {
  flex-grow: 1;
  flex-shrink: 1;
  padding: 5px;
  white-space: nowrap;
}

.flex-child-without-textwrap {
  flex-basis: auto;
}

.flex-child-with-textwrap {
  min-width: 200px;
  flex-basis: 0%;
}

.text-break {
  white-space: pre-wrap;
}

他的解决方案在技术上修复了我在这里提出的问题,但我忘记提到另一个要求:
  • 在 flex-child 中,可以包含任何可换行文本和不可换行文本的组合

当将第三个 flex-child 更改为以下内容时,他的解决方案失败了:

<div class="flex-child flex-child-with-textwrap child-3">
  <div class="text-break">This text should break on new lines when the container shrinks down</div>
  <div>This text should not break on new lines</div>
</div>

在这种情况下,第三个 flex-child 中第二个 div 中的文本溢出其容器,并且文本开始过早换行。
可以在 此处 查看显示几乎正常的解决方案的 CodePen(请注意,因为它在 Chrome 上引起了进一步问题,所以删除了 min-width)。
更新 2: 我认为我之前没有表述清楚:任何一个或全部 flex-children 都可能具有可换行文本。这完全取决于动态生成的内容。在我给出的示例中,只有第三个 flex-child 具有可换行文本,但情况并非总是如此。

请查看此处第三个项目符号中的信息和参考链接:https://dev59.com/E1wY5IYBdhLWcg3wwqL1#39310591 - Michael Benjamin
@Michael_B 感谢您的评论。我已经研究了单位之间的差异,但是它们都不起作用。根据我的研究,人们建议使用0%,所以我坚持使用它。 - Jonathan
我认为这个情况可以使用媒体查询来改变 flex-basis 的值。但是,0% 意味着它们不会被计算到布局流中,这就是为什么 IE 看起来像那样的原因。尝试使用 flex-basis:auto; 并添加一些特定更改项目大小比例的媒体查询。您还可以尝试将 flex-wrap 添加到 flex-child div 中。 - Alex W
@AlexW 感谢您的建议。仅仅更改为 flex-basis: auto 是不起作用的,因为它会停止文本换行,而我不能使用大小比例,因为每个元素都应该适合其内容(现在听起来很棒的是 flex-basis: content)。就我所知,flex-wrap 只影响 flex 容器(即具有 display: flex 的容器)。 - Jonathan
你在第一段链接的那个 bug 正是这个问题的根本原因。 - TylerH
显示剩余2条评论
2个回答

6

注意:本答案发布于问题的2个更新之前,导致部分无效,但我现在将其保留,因为有人可能需要它。

经过多次试验,我想出了这样一个设置,目的是让IE尽可能地模拟其他浏览器的行为。

IE需要在可断行元素的父元素上设置最小宽度,这样在元素换行之前文本就不会折叠成0宽度。由于在一般情况下flex-basis需要设置为auto,但在可断行元素的父元素上必须设置为0px,所以flex-grow的值需要大约为5。

我添加了以下规则,使用了仅适用于IE11的选择器(在我的这个答案中展示)。

_:-ms-fullscreen, :root .flex-child {
  flex-basis: auto;
}
_:-ms-fullscreen, :root .child-3 {
  flex: 5 0 0px;
  min-width: 80px;
}

更新后的CodePen

代码片段

.wrapper {
  width: 80%;
}

.flex-container {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}

.flex-child {
  flex: 1 0 0%;
  white-space: nowrap;
  padding: 5px;
}

_:-ms-fullscreen, :root .flex-child {
  flex-basis: auto;
}
_:-ms-fullscreen, :root .child-3 {
  flex: 5 0 0px;
  min-width: 80px;
}

.text-break {
  white-space: pre-wrap;
}

/*Add backgrounds for display*/
.child-1 {
  background-color: pink;
}
.child-2 {
  background-color: lightblue;
}
.child-3 {
  background-color: lightgreen;
}
<div class="wrapper">
  <div class="flex-container">
    <div class="flex-child child-1">
      <div>This text should not overlay other flex-children.</div>
    </div>
    <div class="flex-child child-2">
      <div>This text should not overlay other flex-children.</div>
    </div>
    <div class="flex-child child-3">
      <div class="text-break">This text should break on new lines when the container shrinks down</div>
    </div>
  </div>
</div>


谢谢您的回答。它非常接近解决初始问题;然而,我在问题的更新中添加了一个额外的要求:“在 flex-child 中,可以有任何可换行文本和不可换行文本的组合”。不幸的是,它没有满足这个额外的要求。除非有更好的解决方案出现,否则我会尝试使用您提出的解决方案。 - Jonathan
@Jonathan 是的,我明白这可能会出现问题,有时候可以解决,有时候不行....所以我很确定你需要一个脚本备用方案来处理所有的异常情况,或者简单地让IE按照它自己的方式呈现。 - Asons
经过一些测试,我发现这个方法可以在我编写的实际应用中工作,但存在两个问题:(1)min-width 值会导致内容溢出(不是在我这里提供的简化示例中)。 (2)假设动态生成的内容在所有三个 flex-children 中都有 wrappable 文本。您的解决方案会再次导致溢出(将 .child-3 CSS 应用于每个 flex-child 时)。 我尝试使用 overflow: auto 来修复其中的一些问题,但它会导致 flex-child 不适当地换行。 - Jonathan

1

这不是首选方案,因为它使用了Javascript。然而,它可以工作。

在创建所有flex子元素之后,每当屏幕尺寸发生变化时,检查每个flex子元素的宽度是否小于其内容的宽度(为了使此方法有效,内容必须用具有display: table-cell属性的元素包装)。如果是这样,请向flex子元素添加等于其内容宽度的最小宽度。一旦添加了最小宽度,就无需再次进行计算,因此不再检查屏幕尺寸更改。仅在IE11上执行此操作。

我使用jquery来实现此解决方案。

HTML:

<div class="flex-container">
    <div id="1" class="flex-child child-1">
        <div class="content-container">This text should not overlay other flex-children.</div>
    </div>
    <div id="2" class="flex-child child-2">
        <div class="content-container">This text should not overlay other flex-children.</div>
    </div>
    <div id="3" class="flex-child child-3">
        <div class="content-container">
            <div class="text-break">This text should break on new lines when the container shrinks down</div>
            <div>This text should not break on new lines</div>
        </div>
    </div>
</div>

CSS:
.flex-container {
    display: flex;
    flex-wrap: wrap;
    width: 100%;
}

.flex-child {
    flex-grow: 1;
    flex-shrink: 0;
    flex-basis: 0%;
    white-space: nowrap;
    padding: 5px;
}

.text-break {
    white-space: pre-wrap;
}

.content-container {
    display: table-cell;
}

Jquery:

if (!!window.MSInputMethodContext && !!document.documentMode) {//if IE11
    $(".flex-child").each(function (i, elem) {
        var $flex_child = $(elem);
        var child_id = $flex_child.attr("id");

        //add resize event to window
        $(window).on("resize." + child_id, function () {
            var width_of_flex_child = $flex_child.outerWidth();
            var content_width = $flex_child.children(".content-container").outerWidth();

            if (width_of_flex_child < content_width) {
                $flex_child.css("min-width", content_width + "px");

                //remove event
                $(window).off("resize." + child_id);
            }
        }).trigger("resize." + child_id);
    });
}

可以在此处找到查看此解决方案的CodePen 链接

不幸的是,由于窗口事件,此解决方案需要额外的开销来管理资源。


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