疑似要素で解決!Flexboxで均等配置して、最後を左詰めにする方法【space-between】

Flexboxのjustify-contentに【space-between】を指定すると、とても簡単に要素を均等配置させることができます。

レイアウトを組む時に、超絶便利なspace-betweenですが、1つだけ問題があります。それは複数行の時に左寄せにならない!ということです。

例えば、横に3つ並ぶレイアウトで子要素を5個入れてみます。
すると、3つ目まではきれいに配置されていますが、space-betweenの「指定した要素が最初と最後の端に寄る」という特徴により、4個目と5個目は左と右に寄ってしまいます。

端っこにいくなやー!!!

記事一覧などでFlexboxを一番使いたいのに、記事数によってはレイアウトが崩れてしまうのはNG。かといって、そこだけinline-blockやfloatで組むのも微妙なところ…なんとかしたい。

ということで!今回は「Flexboxで均等配置して、最後を左詰めにする方法」をご紹介します!

💡 現代的な代替案: gapプロパティを使えばそもそも疑似要素は不要

この記事のテクニックは2020年頃の情報ですが、今はjustify-content: space-between;を使わず、gapプロパティで間隔を作る書き方が主流です。要素の幅を100%にせず、間隔をgapで確保するだけで、そもそも最後の行が両端に開いてしまう問題自体が起こりません

.flexBox {
  display: flex;
  flex-wrap: wrap;
  gap: 20px; /* 要素間の間隔をここで確保する */
}

.flexItem {
  width: calc(33.333% - 14px); /* 3カラムの場合。gap分だけ差し引く */
}

justify-content: space-between;を使わない(初期値のflex-startのまま)ので、要素は常に左詰めになり、疑似要素で穴埋めする必要も、カラム数ごとに書き方を変える必要もありません。これから新しく実装するなら、まずこちらの方法を検討してください。

とはいえ、space-betweenを使った既存のコードを保守する場合や、gap未対応の古い環境を考慮する必要がある場合のために、以下では元々の疑似要素を使った方法も解説します。

目標物

今回のゴールは、下の画像のように、Flexboxのspace-betweenを使って複数行の時でも、要素が左寄せになっていることです。

今回の重要ポイントは「疑似要素」です!

3カラムの場合、4カラムの場合、5カラム以上の場合と子要素の数によって対応方法が変わってくるので、それぞれ説明していきます!

HTML

基本となるHTMLです。カラム数に応じて、中のアイテム数を調節してください。

<div class="flexBox">
    <div class="flexItem">item1</div>
    <div class="flexItem">item2</div>
    <div class="flexItem">item3</div>
    <div class="flexItem">item4</div>
    <div class="flexItem">item5</div>
</div>

CSS

基本となるCSSです。
※SCSSで表記しています。

.flexBox {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    width: 100%;
    max-width: 960px;
    margin: 0 auto;
    padding: 15px;
    box-sizing: border-box;

    .flexItem {
        width: 30%;
        padding: 50px 0;
        margin-bottom: 20px;
        text-align: center;
        background: gray;
        color:white;
    }
}

3カラムの場合

3カラムの場合はflexを指定している親要素に疑似要素afterをつけて、見えないボックスを作ってあげます。

MEMO

ポイントは疑似要素の幅を子要素の幅と同じ数値にすることです。
違う数値にすると・・・・・ずれます!

CSS

.flexBox {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    width: 100%;
    max-width: 960px;
    margin: 0 auto;
    padding: 15px;
    box-sizing: border-box;

//----疑似要素を追記----
    &::after {
        content: "";
        width: 30%;
    }
//-------------------

    .flexItem {
        width: 30%;
        padding: 50px 0;
        margin-bottom: 20px;
        text-align: center;
        background: gray;
        color:white;
    }
}

疑似要素を入れてあげると、2行目の中の要素が両端に行くことはなく、左詰めになりました。

See the Pen Flexboxで左寄せ(3カラム) by design-levelup (@marurain) on CodePen.

4カラムの場合

先ほどの3カラムの場合は疑似要素のafterで対処しましたが、4カラムの場合はbefore要素も使っていきます。

CSS

基本のCSSは一緒ですが、要素を4つ横に並べるのでwidthを22%としました。

更に今回は、要素がspace-betweenにより両端にいかないようにするために、見えないボックスを疑似要素beforeも使用して、計2つ用意してあげる必要があります。

MEMO

before要素はafterと同じように入れただけだと親要素の前に入ってしまうのでorderで表示順を指定してあげましょう!
※orderを指定した要素は、orderを指定していないアイテムの後ろに表示さます。(指定しないものは見えないだけでデフォルト値:0が設定されている)

.flexBox {
    display: flex;
    justify-content: space-between;
    flex-wrap: wrap;
    width: 100%;
    max-width: 960px;
    margin: 0 auto;
    padding: 15px;
    box-sizing: border-box;

    //----疑似要素を追記----
    &::before {
        content: "";
        width: 22%;
        order: 1;
    }

    &::after {
        content: "";
        width: 22%;
    }

    //-------------------

    .flexItem {
        width: 22%;
        padding: 50px 0;
        margin-bottom: 20px;
        text-align: center;
        background: gray;
        color: white;
    }
}

要素が6つで2行になった場合でも、beforeとafterで作成した見えないボックスが並んでいるので、要素が両端に行かなくなりました。

See the Pen QWyRPap by design-levelup (@marurain) on CodePen.

5カラム以上の場合

3カラム、4カラムの場合は疑似要素を駆使して対処してきましたが、5カラムになってくると、疑似要素の数が足りないため、HTMLで空のボックスを入れておくかJavascriptを使う必要があります。

実は、カラム数が増えても対応できる、もっと汎用的な方法があります。それがHTMLに「見えないダミー要素」を必要な数だけ用意しておく方法です。

HTML: ダミー要素を(カラム数 – 1)個追加する

本物のアイテムの後ろに、「カラム数 − 1」個のダミー要素を追加しておきます。5カラムなら4個、6カラムなら5個です。

<div class="flexBox">
    <div class="flexItem">item1</div>
    <div class="flexItem">item2</div>
    <div class="flexItem">item3</div>
    <div class="flexItem">item4</div>
    <div class="flexItem">item5</div>
    <div class="flexItem">item6</div>
    <div class="flexItem">item7</div>
    <!-- ここから5カラム分のダミー要素(4個) -->
    <div class="flexItem dummy"></div>
    <div class="flexItem dummy"></div>
    <div class="flexItem dummy"></div>
    <div class="flexItem dummy"></div>
</div>

ダミー要素は見た目には表示させたくないので、CSSで高さと余白を潰して透明にします。

.dummy {
    visibility: hidden;
    height: 0;
    padding: 0;
    margin-bottom: 0;
}
💡 なぜ(カラム数 – 1)個でいいのか

最後の行にできる「空きスペース」は、どんなにアイテム数が半端でも、最大で「カラム数 − 1」個分にしかなりません(ぴったり割り切れる時は0個)。あらかじめこの最大数のダミー要素を並べておけば、実際の空きが何個であっても、ダミー要素がそのぶんだけ場所を埋めてくれるので、アイテムの合計数を気にせず常に左詰めになります。3カラム・4カラムの時のように、カラム数ごとにbefore・afterの書き方を変える必要もありません。

この方法は疑似要素(before・after)ではなく本物のHTML要素を使うため、5カラム以上でも、カラム数が変わっても、同じ考え方でそのまま対応できます。ただし、記事数が動的に変わるWordPressのループなどでダミー要素の数をPHP側で計算して出力する、といった実装が必要になる点は覚えておいてください。

まとめ

  • 新規実装なら、まずgapプロパティを使う方法を検討する(そもそも最後の行が両端に開く問題が起きない)
  • 既存コードの保守でspace-betweenのままにする場合、3カラムは::after、4カラムは::before+::after(order指定)の疑似要素で対応できる
  • 5カラム以上、またはカラム数を固定したくない場合は、「カラム数 − 1」個の見えないダミー要素をHTMLに追加する方法が汎用的
  • ダミー要素はvisibility: hidden;height: 0;で場所だけ確保し、見た目には影響させない

1 COMMENT

bedava

Major thankies for the blog article. Much thanks again. Keep writing. Debera Daniel Polivy

現在コメントは受け付けておりません。