こちらは、 CAMエンジニア Advent Calendar 2019 8日目の記事です。
https://qiita.com/advent-calendar/2019/cam-inc
昨日は @ruman さんの
JavaScriptの基本文法
でした。
自分もフロントエンドをやっていて、「なんだこれ!」となったところだったので、
非常におもしろかったです!
詳しくは記事見てください笑
今回は、flex boxをつかってレイアウトしているときに、
「なぜか width: 0
をつけないとうまくできないなぁ」
って思う事件に何度も遭遇したので、
あらためて、flex boxとはなんぞやも含めて研究(大袈裟ですがw)してみました。
基本的なところは
こちらのサイトが個人的に一番わかりやすいので、参考にしてみてください。
最後まで、読むとガッカリするかもなので、先に結論を述べると、問題の原因は解明しきれませんでした。
ただし、原因究明のためにいろいろ調べた結果、学びが深かったので是非知ってもらいたい!!
では、研究開始です!
どんなときに、width: 0
が必要になるの?
実際に業務で困ったのは、こんな感じのレイアウトを組んで行くときでした。
問題の原因は、display: flex
なっている要素が入れ子になっているとき、
内側の要素にflex-basis: 0
が効かないということです。
(背景の黒い要素も、緑の帯と写真も横並びにしたい)
width: 0
を指定しないとがっつり画面の幅をはみ出してくる
そもそもflex-basisとは
問題を解決するため、ここからちゃんとしらべてみました。
W3Cの仕様書をみると要素には写真のようにいろいろな方向に対して定義がされています。
flex-basis
は、main axis
方向に対する幅を指定しているのです。
さらに、main-axis
は、flex-direction
でrow
にするかcolumn
にするかでその方向が変わります。
(むしろ、flex-direction
は、main-axis
の方向を垂直方向変えているということでしょう。しらんけど)
それに対して、width
は常に横方向に対しての幅を指定しています。
flex-basis: auto
(初期値)時の話
For all values other than auto and content (defined above), flex-basis is resolved the same way as width in horizontal writing modes [CSS21], except that if a value would resolve to auto for width, it instead resolves to content for flex-basis. For example, percentage values of flex-basis are resolved against the flex item’s containing block (i.e. its flex container); and if that containing block’s size is indefinite, the used value for flex-basis is content. As another corollary, flex-basis determines the size of the content box, unless otherwise specified such as by box-sizing [CSS3UI].
>~~~
`flex-basis` を指定しないまたは、`auto`の時は、`widthの値`が入ります。
さらに言えば、`width: auto`の時は、`content`の幅が実質`width`になるので、`flex-basis`も`content`の幅になります。
初めは`flex-basis`での指定された分、`main axis`方向に大きさを取ってくれます。
そして、`flex-grow`を指定していると、親要素の`main axis`方向に伸びれるだけ伸びてくれます。
ただし、`min-width`または`max-width`(←`flex-direction:row`の時)が指定されている場合は、そちらが限界値として最優先されます。
こうして計算された値が、その要素の幅(`flex-direction:column`なら高さ)になるはずです。
(ただし、ここが次の章で問題になります。)
# 今回問題になったやつ
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="nonoakij" data-slug-hash="GRgRLdZ" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="不思議なwidth:0 ">
<span>See the Pen <a href="https://codepen.io/nonoakij/pen/GRgRLdZ">
不思議なwidth:0 </a> by nonoakij (<a href="https://codepen.io/nonoakij">@nonoakij</a>)
on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
こちらで、`.title`に当たっている`width: 0`を消してみると、緑色の帯が画面の横幅よりも伸びきってしまいます。
(文字が全て表示されてしまう。本当は、画面幅を超える場合は[...]でカットしたい。)
`max-width`を指定してもうまくいかず、`flex-basis: 0`を指定してもうまくいかない。
しかしながら、なぜか`width: 0`をあてるとうまくいく、、、
上記記載の通り、`width`に指定した値は、`flex-basis`を指定しない場合は、`width`の値になるので、`flex-basis: 0`を指定しても同義なはずなのに、、、
# 考察とまとめ
こうなる原因として
1. コンテンツの幅の計算タイミングが`width`と`flex-basis`で異なるため
2. 入れ子になった時、内側にあてた`flex-grow`は、一番外側の要素の幅の限界まで伸びれてしまう。(実際、上の要素に`max-width`を指定すれば、それ以上は伸びなかった。
このいずれか、もしくは両方が関係してそう。
不明な点としては
1. なぜか、`flex`の入れ子になっていなければ、この問題は起こらない
ということ。
今回、アドベントカレンダーの期限までに完璧問題の究明はできなかったが、
- どんなときにこの問題が起こるのか?
- そもそも、`flex-basis`と`width`の関係はなんぞや?
- 原因はどこにありそうなのか?(growの方が問題あるかも?とか)
までは探ることができた。
引き続き、原因をしらべて、わかり次第追記したい。
次回は@ohteruさんの記事です!お楽しみに!
# 参考文献
https://drafts.csswg.org/css-flexbox-1/
https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Using_CSS_flexible_boxes
https://developer.mozilla.org/ja/docs/Web/CSS/flex
https://developer.mozilla.org/ja/docs/Web/CSS/flex-grow
https://developer.mozilla.org/ja/docs/Web/CSS/flex-basis
https://gedd.ski/post/the-difference-between-width-and-flex-basis/
https://www.webcreatorbox.com/tech/css-flexbox-cheat-sheet
追記 2019/12/18
これが参考になる気がする
https://qiita.com/mpyw/items/dfc63c1fed5dfc5eda26
追記 2020/01/10
関係あるかも!?
flexにおけるmin-widthについて
min-widthは基本的にデフォルト0だが、flexの時はautoになる!?
https://drafts.csswg.org/css-flexbox-1/#min-size-auto
https://makandracards.com/makandra/66994-css-flex-and-min-width