横並びに並べたアイテム内の特定の要素の高さを揃えたいということがたまにあるので、CSSで対応する方法について検討したことをメモ。
サンプルコード
以下のような構造で、.ttlが複数行になった場合に他の.ttlの高さを揃えたいとします。
<div class="list">
<div class="item">
<div class="img"></div>
<div class="ttl">タイトル</div>
<a class="btn" href="#">詳細</a>
</div>
~ 略 ~
</div>
まずは対応前で、flexboxで横並びにしてみます。
必要な記述のみ抜粋しているので、CSS全体はデモページのファイルをご確認ください。
.list {
display: flex;
flex-wrap: wrap;
}
.item {
width: calc(25% - 40px);
margin: 20px;
}
これで横並びにできましたが、.ttlが複数行の場合に高さは揃いません。
flexboxで対応前のデモページ
対応方法としては、横並びになっている.item内をflexboxで縦並びにして、.ttlをflex-grow: 1 で高さを広げるようにします。
.list {
display: flex;
flex-wrap: wrap;
}
.item {
display: flex;
flex-direction: column;
width: calc(25% - 40px);
margin: 20px;
}
.ttl {
flex-grow: 1;
}
これで.ttlが複数行になったときに、他の.ttlの高さが揃いました。
flexboxで対応後のデモページ
flexboxではなくgridの場合も同様に試してみます。
まず対応前です。
.list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 40px;
}
これでgridを使った横並びにできました
gridで対応前のデモページ
gridの場合も基本的な考え方はflexboxと同じで、gridを入れ子にして対応します。
.list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 40px;
}
.item {
display: grid;
grid-template-rows: auto 1fr auto;
width: 100%;
}
.ttl {
min-height: 0;
align-self: stretch;
}
これでgridの場合も.ttlの高さが揃うようになりました。
gridで対応後のデモページ
うまくいかない例
上記例の場合は後続する要素がボタンで高さが変わらないので問題ないのですが、テキストなど高さの変動する要素の場合は注意が必要です。
<div class="list">
<div class="item">
<div class="img"></div>
<div class="ttl">タイトル</div>
<div class="desc">説明文</div>
</div>
~ 略 ~
</div>
後続要素がテキストで他より行数が増えた(高さが増えた)場合、その分.ttlの高さが減るため、後続要素の先頭位置はそろわなくなります。
flexboxで子要素の高さが揃わない場合のデモページ
これはgridでの実装の場合も同様です。
gridで子要素の高さが揃わない場合のデモページ
対応方法
後続要素の高さを問わずに高さを揃えたい場合、gridの実装でsubgridを使用します。
subgridは記事作成時点で主要ブラウザで対応していますが、chromeは117(2023年9月)から対応、Safariは16.0(2022年9月)から対応となっているので、使用の際はご注意ください。
subgridでの実装に変更してみます。
.list {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 40px;
}
.item {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
gap: 0;
width: 100%;
}
.ttl {}
これで後続の要素の高さにかかわらず、要素の高さを揃えることができました。
subgridで高さを揃えるデモページ1
subgridでの実装の場合、高さを揃えたい要素が複数ある場合でも対応できます。
.tagsを追加して、複数のタグが表示される想定にしてみます。
<div class="list">
<div class="item">
<div class="img"></div>
<div class="tags">
<div class="tag">タグA</div>
</div>
<div class="ttl">タイトル</div>
<div class="desc">説明文</div>
</div>
~ 略 ~
</div>
行数が4行になったので、grid-rowの値を変更します。
.item {
display: grid;
grid-template-rows: subgrid;
grid-row: span 4;
gap: 0;
width: 100%;
}
タグの数が多くて2行になる場合も、.tagsの高さを揃えることができました。
subgridで高さを揃えるデモページ2
対応ブラウザや構造などの問題でsubgridを使えない場合もありうるので、最後にCSSではなくJavaScriptで対応する方法を試してみます。
高さを揃えたい要素にdata属性を付与します。
<div class="list">
<div class="item">
<div class="img"></div>
<div class="ttl" data-align_height="ttl">タイトル</div>
<div class="desc">説明文</div>
</div>
~ 略 ~
</div>
JavaScriptで高さを揃える関数を作成して、ページ読み込み時とリサイズ時に実行するようにします。
align_elements_height();
window.addEventListener('resize', align_elements_height);
function align_elements_height() {
const dalignDataAttr = 'data-align_height';
const alignTargets = document.querySelectorAll(`[${dalignDataAttr}]`);
const alignTargetsVals = Array.from(alignTargets).map(target => target.dataset.align_height);
const align_groups = Array.from(new Set(alignTargetsVals));
align_groups.forEach(group => {
const groupTargets = document.querySelectorAll(`[${dalignDataAttr}="${group}"]`);
groupTargets.forEach(el => el.style.height = '');
const max_height = Math.max(...Array.from(groupTargets).map(el => el.clientHeight));
groupTargets.forEach(el => el.style.height = max_height + 'px');
});
}
これで要素の高さ揃えができました。
JavaScriptで高さを揃えるデモページ1
data属性の値を変更することで、複数要素の高さ揃えもできます。
<div class="list">
<div class="item">
<div class="img"></div>
<div class="tags" data-align_height="tags">
<div class="tag">タグA</div>
</div>
<div class="ttl" data-align_height="ttl">タイトル</div>
<div class="desc">説明文</div>
</div>
~ 略 ~
</div>
タイトルとタグの高さを揃えることができました。
JavaScriptで高さを揃えるデモページ2
コメントが承認されるまで時間がかかります。