Flex 布局子元素超宽问题

之前在做项目时,就曾碰到过一个问题:flex子元素宽度过大时,flex-shrink 并不能限制子元素宽度,导致子元素宽度超出了父元素的宽度。

而最近刚好又碰到这个问题了,就简单记录下,以防遗忘。

1. 问题表现

表现就是子元素宽度不受 flex-shrink 影响,未按照预期进行收缩,比如下面情况:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>flex-shrink</title>
  <style>
    .wrapper {
      display: flex;
      width: 400px;
      border: 1px solid black;
    }

    .left {
      flex-grow: 0;
      flex-shrink: 0;
      width: 100px;
      background: green;
    }

    .right {
      flex-grow: 1;
      flex-shrink: 1;
      background: teal;
    }

    .content {
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="left"></div>
    <div class="right">
      <div class="content">
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      </div>
    </div>
  </div>
</body>
</html>

2. 问题分析

从现象上看,子元素根本没有进行预期的收缩,即使我们设置了 flex-shrink。

现在我们换一种思考方式,我想要某个元素 width 在收缩时,不允许小于某个值,你会怎么做?当然是设置 min-width。一旦设置了 min-width,元素的宽度变化就不会小于这个值了。

到了这里,我们其实大概能知道为什么元素没有收缩了,因为元素的 min-width 不允许它收缩。这里也就得出了结论,其实 flex-shrink 仍然受元素自身的尺寸影响,即如果我设置了 min-width,那无论如何,flex-shrink 也不会收缩到比 min-width 还小的尺寸。

比如下面的例子:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>flex-shrink</title>
  <style>
    .wrapper {
      display: flex;
      width: 400px;
      border: 1px solid black;
    }

    .left {
      flex-grow: 0;
      flex-shrink: 0;
      width: 390px;
      background: green;
    }

    .right {
      flex-grow: 1;
      flex-shrink: 1;
      min-width: 20px;
      height: 20px;
      background: teal;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="left"></div>
    <div class="right"></div>
  </div>
</body>
</html>

所以,flex-shrink 是受元素 min-width 影响,且不能比其更小。回到上面的问题,子元素没有按照预期收缩也是能理解的了,默认的 min-width 的值是 auto,即为 width 的尺寸,而 width 的尺寸则为元素的默认大小,就是文字全部展开的大小,所以导致 flex-shrink 无法生效。

3. 解决方案

知道上面的原因后,解决方案也很简单,设置 min-width: 0 即可。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>flex-shrink</title>
  <style>
    .wrapper {
      display: flex;
      width: 400px;
      border: 1px solid black;
    }

    .left {
      flex-grow: 0;
      flex-shrink: 0;
      width: 100px;
      background: green;
    }

    .right {
      flex-grow: 1;
      flex-shrink: 1;
      min-width: 0;
      background: teal;
    }

    .content {
      text-overflow: ellipsis;
      white-space: nowrap;
      overflow: hidden;
    }
  </style>
</head>
<body>
  <div class="wrapper">
    <div class="left"></div>
    <div class="right">
      <div class="content">
        aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
      </div>
    </div>
  </div>
</body>
</html>