Flexbox で全体を中央に配置しつつ最後の行を左揃えにする

# 目的

以下のように、親パネルの中に子パネルが 12 枚並んでいます。

![スクリーンショット 2016-02-05 22.30.08.png](https://qiita-image-store.s3.amazonaws.com/0/6697/8fc219ef-7e58-687c-58a8-9caa826eca2a.png)

[Flexbox (CSS Flexible Box Layout)](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout) を使って子パネルをこの画像の通りに並べたいです。

要件は次の 2 つです。
1. 子パネル全体は親パネルの左右中央に配置しつつ、子パネルは左から順番にならべたい。
2. それぞれの子パネルの行と列を整列させたい。

HTML は以下の通りです。

```html
<ul class="grid">
  <li class="cell">ゆの</li>
  <li class="cell">宮子</li>
  <li class="cell">沙英</li>
  <li class="cell">ヒロ</li>
  <li class="cell">乃莉</li>
  <li class="cell">なずな</li>
  <li class="cell">茉里</li>
  <li class="cell">まどか</li>
  <li class="cell">さやか</li>
  <li class="cell">マミ</li>
  <li class="cell">杏子</li>
  <li class="cell">ほむら</li>
</ul>
```

# 試行錯誤

まず、以下のように CSS を書きます。

```css
/* 親パネル */
.grid {  
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
}
```

![スクリーンショット 2016-02-05 22.30.41.png](https://qiita-image-store.s3.amazonaws.com/0/6697/5a29f882-db17-4ba3-8891-47ef2294f1c1.png)

これは `justify-content: center;` を指定することで、子パネルを中央に配置しています。しかし、最後の行の 2 つの子パネルが中央に寄ってしまうことで、列が揃わなくなってしまいます。:cold_sweat: 

では `justify-content: flex-start;` (justify-content の初期値) を指定するとどうなるでしょうか？

```css
/* 親パネル */
.grid {  
  display: flex;
  flex-flow: row wrap;
  justify-content: flex-start;
}
```

![スクリーンショット 2016-02-05 22.41.52.png](https://qiita-image-store.s3.amazonaws.com/0/6697/a775480b-001e-04aa-23eb-76c53b2bc3c6.png)

行と列はきれいに揃うのですが、もちろん左寄せになってしまいます。:cold_sweat:

# 解決策

Flexbox でなんとかこの目的を達成できないかとネットの海をさまよい続け、ようやく解答を得ました。
ただし、Flexbox あるいは CSS だけでもどうにもなりませんでした。

発見したのは Stack Overflow の以下の投稿です。

<i class="fa fa-fw fa-stack-overflow"></i> [Flex-box: Align last row to grid](http://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid)

ここで提示されている解決策のひとつが、

> One technique would be inserting a number of extra elements (as many as the max number of elements you ever expect to have in a row) that are given zero height. Space is still divided, but superfluous rows collapse to nothing

という、最後の行の残りの子要素を JavaScript (jQuery) を使って空の子要素で埋める方法です。空の子要素を実際の子要素の数だけ末尾に追加することで、たとえ親要素の width が変わって 1 列の子要素の数が変動したとしても、きちんと実現できるようになっています。

僕の挙げた例でも、実際に空の子パネルを親パネルに追加することで対応してみました。

```css
/* 親パネル */
.grid {  
  display: flex;
  flex-flow: row wrap;
  justify-content: center;
}

/*
 * 空の子パネル
 * padding, margin の左右も 0 に指定してしまうと、
 * 最後の行のレイアウトが崩れるので注意。
 */
.cell.is-empty {
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  margin-top: 0;
  margin-bottom: 0;
}
```
```js
var $grid = $('.grid'),   
    emptyCells = [],
    i;

// 子パネル (li.cell) の数だけ空の子パネル (li.cell.is-empty) を追加する。
for (i = 0; i < $grid.find('.cell').length; i++) {
    emptyCells.push($('<li>', { class: 'cell is-empty' }));
}

$grid.append(emptyCells);
```

すると結果はこのようになりました。期待通りに並んでいます。 :smiley: 

![スクリーンショット 2016-02-05 22.57.00.png](https://qiita-image-store.s3.amazonaws.com/0/6697/1f2c07fc-caa5-063f-4e63-5863d2642d4f.png)

親パネルの幅が変わっても期待通りに表示されます。:blush: 

![スクリーンショット 2016-02-05 22.58.25.png](https://qiita-image-store.s3.amazonaws.com/0/6697/5e32818f-d402-dfd9-0897-f8f5e808a143.png)

# 感想

CSS だけで完結しなかったり、JavaScript で無駄な要素を追加する必要があったりしたのは残念です。正直、ワークアラウンド感が否めない対応ですね。しかし、JavaScript で要素の位置や幅、余白を計算したり調整したりすることなく実現できたのはよかったです。

また「JavaScript を使って末尾に透明な子要素を追加すればよさそう」ということはなんとなくイメージできていました。しかし「高さや余白が 0 の子要素を実際の子要素数だけ用意すれば、親要素の幅に依存せずに期待通りに表示される」というところまで考えが至りませんでした。そのため、件の Stack Overflow の投稿内容が非常に有用でした。

# おまけ

今回の検証に用いたコードを CodePen に上げています。

<p data-height="265" data-theme-id="0" data-slug-hash="bEmVLo" data-default-tab="html,result" data-user="quanon" data-embed-version="2" data-pen-title="Flexbox (justify-content: center AND align left last row)" class="codepen">See the Pen <a href="https://codepen.io/quanon/pen/bEmVLo/">Flexbox (justify-content: center AND align left last row)</a> by QUANON (<a href="https://codepen.io/quanon">@quanon</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>