通常、ウェブのレイアウトにおいて高さ揃えというと水平方向に並んだ要素のうち一番高い要素にあわせて他の要素の高さを揃えることがほとんどかと思いますが、今回は高さが低い要素に合わせて高さを揃える実装が必要になりました。
2017年にもなって高さ揃えの話かとは思いつつ、検索してもこれと言った方法が見つからなかったため実際に実装した方法を紹介します。
要件
具体的には、左カラムに高さが可変のニュースエリアがあり右カラムにバナー画像が並ぶレイアウトで、バナーの数は2 〜 4個。こちらも高さが可変します。全体の高さはバナーがある右カラムに依存し、左のニュースカラムが右カラムより高くなる場合は高さを右カラムに合わせて、はみ出したコンテンツを閲覧するためにスクロールバーを出す、という要件です。
なお、仮に左カラムのコンテンツが少なく右カラムの方が高くなった場合は、右カラムの高さを低くして合わせるのではなく単に高さを揃えるのみとします。
実装
HTMLの構成は、カラムをラップする.wrapperと、高さが可変する.masterカラム、.masterカラムの高さに追随する.slaveカラムを用意し、.slaveカラムにはインナー要素(.slave-inner)を追加し、それぞれの中にコンテンツを配置します。
<div class="wrapper">
<div class="slave">
<div class="slave-inner">
content ...
</div>
</div>
<div class="master">
content ...
</div>
</div>
CSSは、.wrapperをフレックスコンテナーとし、.masterと.slaveの高さを揃えます。ただし、このままでは、.slaveの高さが.masterより高くなった場合、.slaveに高さが揃ってしまいます。
そこで.slaveカラムの高さを自身の子要素の高さに依存させないために、.slave-innerにheight:0を指定します。これにより.slaveは高さを持つ子要素が無いため、高さは.masterの高さに依存することになります。
最後にはみ出た内容を要素上のスクロールで見せるためにoverflow-y:scrollを与えると、要件を満たしたレイアウトが実装できます。
.masterの高さがあまりに少なく文章が読みにくくなってしまうケース(運用上は想定外だがバナーが1個になってしまったケースなど)に備えて.slaveにmin-heightを指定するか、.slave-innerにある程度の高さを与えても良いでしょう。
.wrapper {
display: flex;
}
.slave {
overflow-y: scroll;
min-height: 5em;
}
.slave-inner {
height: 0;
}
デモ
See the Pen Height adjust low colum by masuP9 (@masuP9) on CodePen.
本実装の問題点
本実装には、次の問題点があります。
- 高さを合わせる要素を事前にマークアップで指定する必要がある
-
.slaveのpadding-bottomが無視される
前者の「高さを合わせる要素を事前に指定する必要がある」は、対象の要素群に対して、.masterと.slaveというクラスを事前に割り振る必要があるため、要素を高さを合わせる要素を指定しておかなければならず一番低くなる要素が不確定の場合に対応できない、という問題です。
今回は「右のバナーカラムの高さが左のカラムより低くなった場合、左のカラムの高さは合わせなくてよい」という要件だったため問題ありませんが、一番低くなる要素が不確定の場合は世に普及している、JavaScriptで要素の高さを比較して高さを揃えるライブラリのロジックを修正することでおそらく実現できるはずです。
後者の「.slaveのpadding-bottomが無視される」は、overflow:scrollを使用しているためですが、デザインによっては、余白は.wrapperに持たせたり.slave-inner直下の最後の要素に余白を追加したりすることで解決できます。
まとめ
本実装の要点をまとめると、「高さを合わせる要素にインナー要素を設置し、インナー要素の高さを0 or 計算外にし各要素の高さを揃える。そのため事前に高さを合わせる要素を指定する必要がある。」となるでしょうか。
フレックスボックスが使用できるようになり高さ揃えに悩むことは少なくなりましたが、高さが低い方に合わせるという要件の場合は、まだ少し悩まされるかもしれません。