Pinterest風のグリッドレイアウトをCSSのみで実装?乗り越えられない壁、代替策はないのか?Pinterstレイアウトを考察

JavaScript

Webに携わっている方は必ず一度は「Pintarestっぽく出来ませんか?」的なことを言われると思います。僕個人としてはPintarestのようなグリッドレイアウトはあまり好みではありません、なんせ「めんどくさい!」から。

Pintarestのように半無限スクロールで実装するのはかっこいいですが、無限スクロール出来るほど中身のないコンテンツをピンタレストっぽく配置してもいまいちピンと来ないんですよね(ピンタレストだけに)。

「Pintarest風 CSS」「Pintarest風 レイアウト CSS」といったGoogleサジェストが良く出てきますが、Pintarestを再現するのはCSSだけでは不可能と思われてきました。

私の知る限りではCSSのみでの実装は、大枠のコンテンツの高さが決め打ちされているときだけです。flexもfloatも並びに食い込んでいく仕様には出来ませんよね。一見出来ているようにも見えますが、要素を追加する度に大枠を囲っている要素の高さも変えてあげる必要があります。運用性ゼロですね。

「Pintarest風レイアウトをCSSだけで実現してみよう」的な記事がネット上に散見されますが、「左上から方向に配置していって、決め打ちした高さにたどり着いたら右のカラムに移動して…」を繰り返すんですよね。

これだと本当の意味でPintarestには程遠くなります。だって最後のカラムでブロックが多いと一番右のカラムだけ突き抜けて下向きに重なるだけですから……
overflow:hidden;なんてしちゃったら最終カラムの下が途中で切れちゃいますよ。

しかし、ここにきてCSSのみでピンタレストっぽくレイアウトが(高さの指定なしで)実現可能なことが分かりました。あくまで「っぽく」ですが十分実用に耐えると思われます。

CSSプロパティ「column-count」を使う

SNSでcolumn-count: 3;とbreak-inside: void;を使った例が画面キャプチャ付きで投稿されていました。え、マジ、、、となったので早速検証してみましょう。

1111111111111111111111111111111111111111111111111111111111111111111111
22222222222222
333333333333333333333333333333333333333333333333
444444444444444444444444444444444444
55555555555555
666666666666666666666666666666666666666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777777777777vvvv
888888888888888888888888888888888888
999999999999999999999999999999999999999999999999999999999
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1212121212121212121212121212121212121212121212121212121212
131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313
1111111111111111111111111111111111111111111111111111111111111111111111
22222222222222
333333333333333333333333333333333333333333333333
444444444444444444444444444444444444
55555555555555
666666666666666666666666666666666666666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777777777777vvvv
888888888888888888888888888888888888
999999999999999999999999999999999999999999999999999999999
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1212121212121212121212121212121212121212121212121212121212
131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313

このツイートもっとバズっていいでしょうよ。素敵よ。マジ。いい感じ!

  <div class="container-breakinside">
    <div class="item1">1111111111111111111111111111111111111111111111111111111111111111111111</div>
    <div class="item2">22222222222222</div>
    <div class="item3">333333333333333333333333333333333333333333333333</div>
    <div class="item4">444444444444444444444444444444444444</div>
    <div class="item5">55555555555555</div>
    <div class="item6">666666666666666666666666666666666666666666666666666666666666666666666666666666666666</div>
    <div class="item7">77777777777777777777777777777777777777777777777777777777777vvvv</div>
    <div class="item8">888888888888888888888888888888888888</div>
    <div class="item9">999999999999999999999999999999999999999999999999999999999</div>
    <div class="item10">101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010</div>
    <div class="item11">1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</div>
    <div class="item12">1212121212121212121212121212121212121212121212121212121212</div>
    <div class="item13">131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313</div>
    <div class="item1">1111111111111111111111111111111111111111111111111111111111111111111111</div>
    <div class="item2">22222222222222</div>
    <div class="item3">333333333333333333333333333333333333333333333333</div>
    <div class="item4">444444444444444444444444444444444444</div>
    <div class="item5">55555555555555</div>
    <div class="item6">666666666666666666666666666666666666666666666666666666666666666666666666666666666666</div>
    <div class="item7">77777777777777777777777777777777777777777777777777777777777vvvv</div>
    <div class="item8">888888888888888888888888888888888888</div>
    <div class="item9">999999999999999999999999999999999999999999999999999999999</div>
    <div class="item10">101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010</div>
    <div class="item11">1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111</div>
    <div class="item12">1212121212121212121212121212121212121212121212121212121212</div>
    <div class="item13">131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313</div>
  </div>
.container-breakinside {
column-count: 3; /* こいつが肝 */
height: auto !important; /* 検証として高さは可変(auto)に設定しているだけです。実際は不要です。 */
}
.container-breakinside > div {
height: auto !important; /* 検証として高さは可変(auto)に設定しているだけです。実際は不要です。 */
break-inside: avoid; /* こいつが肝 */
margin: 0 0 10px;
background-color: #333;
color: #FFF;
padding: 0.5em 1em;
}

Google Chromeだと右下の余白が大きいのが気になるのでちょっと調整してみました。もしかしたらブラウザによってレンダリング方法(計算方法)が異なるのかも。ただ、大きな崩れは特に無かったので実用に耐えますね。

1111111111111111111111111111111111111111111111111111111111111111111111
22222222222222
333333333333333333333333333333333333333333333333
444444444444444444444444444444444444
55555555555555
666666666666666666666666666666666666666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777777777777vvvv
888888888888888888888888888888888888
999999999999999999999999999999999999999999999999999999999
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1212121212121212121212121212121212121212121212121212121212
131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313
1111111111111111111111111111111111111111111111111111111111111111111111
22222222222222
333333333333333333333333333333333333333333333333
444444444444444444444444444444444444
55555555555555
666666666666666666666666666666666666666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777777777777vvvv
888888888888888888888888888888888888
999999999999999999999999999999999999999999999999999999999
101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1212121212121212121212121212121212121212121212121212121212
131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313
.container-breakinside {
column-count: 3;
/* 略 */
}
.container-breakinside > div {
/* 略 */
break-inside: auto; /* ここをオートに */
/* 略 */
}

ここで注意すべき点は、要素順が縦並びということ。ブロックの中の数字を見てもらえばわかりますが、縦に上から下へ要素が配置されています。特にこだわりが無い場合は良いですが、(例えば)新商品をページの上方に掲載したい場合なんかはちょっと使えないかなという印象です。

これだと「新着商品!」的な要素が全部左に寄ってしまいますね。ケースバイケースで使うのは全然ありだと思います。

グリッドレイアウトの代表格「masonry.js(jQueryプラグイン)」

もう十数年も前でしょうか……NHKの特設サイトに導入されて一躍話題を呼んだ「masonry.js」というプラグインです。masonry.jsについては下記の記事に使い方やサンプルコードを置いているのでそちらを参照してください。

このjQueryのプラグインを使えばある程度Pintarest風に近づけることは出来ます。挙動などはオプションで調整すればそれなりの形になるのではないでしょうか。

ただ、このプラグイン古いので思いがけない不具合等がある可能性も……という噂(マルチブラウザ対応、新バージョン対応が甘い?)を耳にしまして、ちょっと僕が実装するときはこのプラグインは要検証かな、という感じです。結構重めの処理が走るので実機のスペックが低いと表示に時間がかかるかもしれません。

真打登場?「Magic Grid」で可変グリッドレイアウト

2022年9月13日時点ではgitHubのmasterの更新が8カ月前なのでちょこちょこメンテされているみたいです。gitHub見る限り登場は4年前かな。

Magic GridはjQuery無しで単体で動作するため、jQueryを読み込まないで利用できます。そのため、ある程度高速な処理を実現できているので人気が出始めているようです。

以下、実際にMagic Gridの公式ページを元に再現したサンプルレイアウトになります。動的(公式やその他紹介サイトでブロックの高さが可変の時は動的と表現しているようです)の場合に縦のマージンが狂うときがあるので何か微妙な感じかも。初期表示だけおかしいのでブラウザを縮めたりしてみてください。

1111111111111111111111111111111111111111111111111111111111111111111111
22222222222222
333333333333333333333333333333333333333333333333
444444444444444444444444444444444444
55555555555555
666666666666666666666666666666666666666666666666666666666666666666666666666666666666
77777777777777777777777777777777777777777777777777777777777vvvv
888888888888888888888888888888888888
999999999999999999999999999999999999999999999999999999999
101010101010101010101010101010101010101010101010101010101010101010101010101010
1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
1212121212121212121212121212121212121212121212121212121212
131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313

一応ソースを掲載しておきます。

    .container-magic-grid div {
      width: 120px;
      word-break: break-all;
      background-color: antiquewhite;
      display: flex;
      justify-content: center;
      align-items: center;
      border-radius: 8px;
      padding: 0.5em 1em;
    }
  <script src="https://cdnjs.cloudflare.com/ajax/libs/magic-grid/3.2.4/magic-grid.min.js" integrity="sha512-3nXvHrCmVAZqsV3DXj04lk/K1aL1b3mAVj9YPVmJrrYXR9dBEa2Le7klZ8EoAQcj3SnVSeQiXZfs3T4I0lCb7w==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
  <div class="container-magic-grid">
    <div class="item1">11111111111111</div>
    <div class="item2">22222222222222</div>
    <div class="item3">33333333333333</div>
    <div class="item4">44444444444444</div>
    <div class="item5">55555555555555</div>
    <div class="item6">66666666666666</div>
    <div class="item7">77777777777777</div>
    <div class="item8">88888888888888</div>
    <div class="item9">99999999999999</div>
    <div class="item10">1010101010101</div>
    <div class="item11">1111111111111</div>
    <div class="item12">1212121212121</div>
    <div class="item13">1313131313131</div>
  </div>
      const magicGrid = new MagicGrid({
        container: '.container-magic-grid',
        animate: true,
        gutter: 10,
        static: false,
        items: 13 // static: falseを指定する際はブロックの個数も指定しないとダメらしいのでjsで渡す感じですかね
      });

      // magicGrid.listen();
      // Magic Grid公式には↑の記述ですが、ページの読み込みがすべて完了してから実行した方が精度が高いみたい
      window.addEventListener('load', (event) => {
            magicGrid.listen(); // 公式のサンプルを修正してload後に発火すれば安定するっぽい
      });

色々試したところ(サンプルソースにもコメントで書きましたが)、windowのロードが完了してからmagicGrid.listen();を発動させると表示が安定するみたいです。グリッドレイアウト周辺が一瞬崩れるので初期表示の際はdisplay:none;等で隠しておいてあげると綺麗かもしれません。

手軽にPintarestしたいならCSSプロパティ「column-count」、新着順に配置したいならJavaScript「Magic Grid」が良さそう

サンプルソースをご覧いただければわかりますが、これだけ少ないステップ数でグリッドレイアウトが実装できることが最大のメリットかと思います。

注意点としては、Magic Gridを実行するのはwindowのロードが完了してからが安定するということです。DOMの描画後でも大丈夫だと思いますが確実なのはロード完了後ですかね。いずれにしても発火のタイミングにちょこっと注意してみてください。

CSSオンリーでPintarest風にしたい場合は、要素が縦並びに順番に配置されることに注意です。

おすすめとは言ってますが、ブロックの中身がもっと複雑になった時の挙動を試した訳ではないので色々検証してみてください。実務ではこんなシンプルな中身な訳ないので……

もし仕事でMagic Gridを使ったら続報と感想を報告しますね。

タイトルとURLをコピーしました