🥲

Flex item の overflow: hidden が効かない問題の対処方法

2023/02/18に公開

Flexアイテムでoverflow: hiddenを設定しているのにFlexアイテムが縮小されずに文字列がはみ出してしまう問題の原因と対処方法を解説します。

背景

皆さんはFlexを使っていますか? 私はよく使っています。

便利なFlexですが、仕様が複雑なため、理解していないと予期しないデザイン崩れが起こってしまうことがあります。

そのうちの1つが、長い文字列でFlexアイテムが縮小されない問題です。

例えば下記をご覧ください。

.grid {
  display: flex;
  width: 200px; /* 横幅を200pxに固定 */
}

.item {
  padding: 20px;
  border: 1px solid #000;
}

.text {
  overflow: hidden; /* はみ出さない */
  white-space: nowrap; /* 改行しない */
  text-overflow: ellipsis; /* はみ出す部分は三点リーダーにする */
}

.item:nth-child(1) {
  flex-grow: 1;
  flex-shrink: 1;
}

.item:nth-child(2) {
  flex-shrink: 0;
}
<div class="grid">
  <div class="item">
    <p class="text">
      Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch
    </p>
  </div>
  <div class="item">
    <p class="text">Item</p>
  </div>
</div>

世界一長い(執筆時現在)村名である「Llanfairpwllgwyngyll-gogerychwyrndrobwll-llantysilio-gogogoch」は本来3点リーダーで省略され、Grid全体は200 pxに収まってほしいのですが、伸びてしまっています。

原因

この挙動は一見バグのように見えます。しかし、実はFlexの仕様通りの正しい動作をしています。

W3C Candidate Recommendation, 19 November 2018の仕様を見てみましょう。 (§7.1.1. Basic Values of flex)

https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#flex-common

By default, flex items won’t shrink below their minimum content size (the length of the longest word or fixed-size element). To change this, set the min-width or min-height property. (See §4.5 Automatic Minimum Size of Flex Items.)

意訳すると次のとおりです。

デフォルトでは、Flex Item は最小コンテンツサイズ (最も長い単語または固定サイズの要素の長さ) 未満には縮小しない。
これを変更するには、 min-width または min-height プロパティを設定する。 (§4.5 Automatic Minimum Size of Flex Items を参照)

また、 §4.5 Automatic Minimum Size of Flex Itemsに5000文字ぐらいで最小サイズの算出方法についての仕様が書いてあるので、お時間あれば読んでみてください。私は理解するのに30分かかりました。

https://www.w3.org/TR/2018/CR-css-flexbox-1-20181119/#min-size-auto

縮められない理由は次のフローが起因しています。

  1. Flex itemに min-width が設定されていないので、 min-width: auto が設定される
  2. auto の幅を算出するために、コンテンツの幅を算出する
  3. コンテンツのwidthを算出したらXpxだった
  4. min-width: Xpx と見なす
  5. よってwidthが期待しているよりも広くなる

対処方法

縮められない理由として、 min-with: auto が原因であることがわかりました。

W3Cのドキュメントでもオススメされていた通り、先程の例に min-width: 0 を設定してみます。

 .item {
   padding: 20px;
   border: 1px solid #000;
+  min-width: 0;
 }

これで、無事に世界一長い村名が省略されました。

まとめ

  • Flex itemが縮小されなくなった原因を理解できました
  • 思ったどおりの挙動せずに困った時は、軽くW3Cのドキュメントに目を通すと解決することが多いです
  • Llanfairpwllgwyngyll-gogerychwyrndrobwll-llantysilio-gogogochを知るきっかけとなった動画 問題より答えが長い!極限早押しクイズ(Youtube)

Discussion