flex box layoutで中に長いテキストなどを含む幅可変要素のレイアウト

久しぶりにcssレイアウトでハマってしまった。 floatレイアウトは一般的なレイアウトとしていろいろとノウハウが溜まっているように思いますが、flex box layoutはまだまだ思いもよらない挙動をすることがありますね。

今回ハマったのは「flex box layoutで中に長いテキストなどを含む幅可変要素のレイアウト」という一言にするとよくわからない感じですが… 要は2カラムレイアウトで、片方は固定幅、もう片方は可変幅の(幅いっぱいになるような)カラムレイアウトであり、可変幅の方の子要素として長いテキストを含んでいるもののレイアウトです。

単純に考えて↓こんな感じで書けば、1つ目の要素が幅100pxの固定になり2つ目の要素が残りをエリアの幅になると思うのですが、そうはなりません…。

<style type="text/css">
.flex {
    display: flex;
    width: 80%;
    border: 1px solid #ccc;
}
.item1 {
    width: 100px;
}
.item2 {
    flex: 1;
    word-wrap: break-word;
}
</style>

<div class="flex">
    <div class="item1">width:100px</div>
    <div class="item2">longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword_longword...</div>
</div>

こんな感じに .flexクラスの要素の幅を大きくはみ出して表示されてしまいます。。。 f:id:kudakurage:20160401224640p:plain

本当は↓こうしたい。 f:id:kudakurage:20160401224727p:plain

まさかflex box layoutでこんな感じに表示が壊れると思っていなかったので困惑…。

解決方法1:overflow: auto

最初の解決方法は、item2の要素のstyleに overflow:autoを加えるという方法です。 この方法で解決はできるんですが、item2の要素の中にツールチップなどのabsolute要素を含んでいてそれがboxをはみ出す位置にあると切れて表示されてしまうという問題が生じてしまいます。 なのでオススメできません。

↓こうなっちゃう f:id:kudakurage:20160401230434p:plain

↓こうしたい(item2のstyleにposition:relative;を指定しなければ、問題はない) f:id:kudakurage:20160401230439p:plain

<style type="text/css">
.flex {
    display: flex;
    width: 80%;
    border: 1px solid #ccc;
}
.item1 {
    width: 100px;
}
.item2 {
    flex: 1;
    word-wrap: break-word;
    overflow: auto;
}
</style>

解決方法2:min-width: 0

2つ目の解決方法は、item2の要素のstyleに min-width: 0を加えるという方法です。 ん?min-widthってデフォルトで0なのでは?と思っていたのですが、よくよく調べてみると、flex itemの場合はデフォルトの値がautoになるんですね…。 min-width: autoってなんだろう??と思ったのですが

auto: flex アイテム向けの既定の最小サイズであり、他のレイアウトにおける既定値 0 よりも合理的な既定値を提供します。

min-width - CSS | MDN

なんかよくわからないですが、いい感じに計算してくれるっていうことなんですかね。今回の場合はいい感じにしてくれませんが。 とにかくこれをmin-width: 0に改めて指定しなおせば希望通りのレイアウトになるようです。

<style type="text/css">
.flex {
    display: flex;
    width: 80%;
    border: 1px solid #ccc;
}
.item1 {
    width: 100px;
}
.item2 {
    flex: 1;
    word-wrap: break-word;
    min-width: 0;
}
</style>

解決方法3:floatレイアウト

flex box layoutにこだわる必要が無いなら、floatレイアウトにしてしまうのが一番ラクで早いです。

<style type="text/css">
.flex::after { /* clearfix */
    display: block;
    clear: both;
    content: '';
}
.item1 {
    float: left;
    width: 100px;
}
.item2 {
    margin-left: 100px;
}
</style>

まとめ

ということで min-width: 0でいい感じに解決できるんですが、いまいち謎な部分もあるので floatレイアウトにしちゃうのがいいんでしょか。兎にも角にも解決しましたし、いろいろと勉強になりました。