はじめに
高さの異なる画像やカードを並べようとして、CSSのflexboxやgridだけでは思い通りの見た目にならず困ったことはないでしょうか。横一列に並べると背の低い要素の下に不自然な余白ができてしまい、かといってcolumn-countでは要素の並び順が縦優先になってしまいます。
Masonryはこの「高さがバラバラな要素を隙間なく敷き詰める」というレイアウト問題を専門に解決してくれるJavaScriptライブラリです。要素を1つずつ最適な位置に配置していくことで、Pinterestのようなカスケード状のグリッドを組み立てられます。
言葉で説明するより、実際に動かしてみたほうが早いと思います。要素を追加してレイアウトが自動で組み直される様子を先に見てみましょう。
Masonryとは
Masonryは、David DeSandu氏が開発した「カスケードグリッドレイアウトライブラリ」です。DOM要素を縦方向の空き具合に応じて配置していくことで、レンガ職人(mason)が石を積むように隙間なく要素を並べます。2010年代から使われ続けている定番ライブラリで、現在もメンテナンスが続いています。
主な特徴
- 依存ライブラリなしで動く - 素のJavaScriptで動作し、jQueryプラグインとしても利用可能
- 列幅・余白を柔軟に指定できる -
columnWidthやgutterオプションでグリッドの見た目を細かく調整できる - 動的な要素の追加・削除に対応 -
appended()やremove()メソッドで、後から要素を増減してもレイアウトが自動で組み直る - 画像の遅延読み込みと相性がよい - 姉妹ライブラリの
imagesloadedと組み合わせることで、画像サイズが確定してから正しくレイアウトできる
インストール
npmを使う場合は次のコマンドでインストールします。
npm install masonry-layout
CDN経由で直接読み込むことも可能です。
<script src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js"></script>
Masonryのサンプルを動かす
以下はMasonryクラスにitemSelector・columnWidth・gutterオプションを渡し、高さの異なる要素をグリッド状に敷き詰めるサンプルです。ボタンを押すとappended()メソッドで要素を追加し、Masonryがレイアウトを自動で組み直します。
import Masonry from 'masonry-layout'
const grid = document.querySelector('.grid')
const msnry = new Masonry(grid, {
itemSelector: '.grid-item',
columnWidth: 100,
gutter: 10,
})
// 要素を追加したらレイアウトに反映させる
const newItem = document.createElement('div')
newItem.className = 'grid-item'
grid.appendChild(newItem)
msnry.appended(newItem)
実際に動かせるものが下です。「要素を追加」ボタンで高さのランダムなカードを増やすと、隙間なく再配置される様子が確認できます。
columnWidth: 100を60に変えると列数が増えて、より密にカードが並ぶようになります。またgutterの値を大きくすると要素同士の間隔が広がります。動的に追加した要素の高さがバラバラでも、Masonryが自動で隙間の少ない位置を見つけて配置してくれるのが分かるはずです。
基本的な使い方
Masonryの初期化はとてもシンプルです。対象のグリッド要素と、アイテムを示すセレクタを渡すだけで動き始めます。
import Masonry from 'masonry-layout';
const msnry = new Masonry('.grid', {
itemSelector: '.grid-item',
columnWidth: 200,
});
HTML側にdata-masonry属性を書く方法もあります。JavaScriptを別途書かずに、オプションをJSON形式で属性値に埋め込めるので、簡単な用途ではこちらが便利です。
<div class="grid" data-masonry='{ "itemSelector": ".grid-item", "columnWidth": 200 }'>
<div class="grid-item">Item 1</div>
<div class="grid-item">Item 2</div>
</div>
実践的なユースケース
画像を含むギャラリーで画像読み込みを待つ
写真ギャラリーでMasonryを使う場合、画像の読み込みが終わる前にレイアウトを計算してしまうと、要素同士が重なってしまう問題が起こります。これは姉妹ライブラリのimagesloadedと組み合わせ、すべての画像が読み込み終わったタイミングでlayout()を呼び直すことで解決できます。
このパターンでは、imagesLoaded(grid, callback)が対象要素配下の全imgの読み込み完了を待ってからコールバックを実行します。画像サイズが確定した後にmsnry.layout()を呼ぶことで、読み込みタイミングのズレによるレイアウト崩れを防げます。
横並びの順序を保つ
デフォルトのMasonryは「空いている列に次々詰めていく」動き方をするため、要素の見た目の並び順が縦方向優先になり、横に並んだときの順序が入れ替わることがあります。番号順やカテゴリ順を保ちたい場合は、horizontalOrder: trueを指定することでソース順を優先した配置に変更できます。
const msnry = new Masonry(grid, {
itemSelector: '.grid-item',
columnWidth: 100,
horizontalOrder: true, // ソース順(横並び優先)でレイアウトする
})
チェックボックスをオンにすると、番号1〜10が縦の見た目より横方向の連番を優先して並ぶように変わります。番号順に読ませたいギャラリーやランキング表示ではhorizontalOrder: trueが有効です。
レスポンシブなグリッド幅への対応
固定のcolumnWidthをピクセルで指定する代わりに、グリッドアイテム自体にCSSで幅を指定し、Masonry側では最初の要素のサイズを基準にする方法もあります。画面幅に応じて列数を変えたいレスポンシブなカードレイアウトに向いています。
.grid-item {
width: 30%; /* 画面幅に応じて割合で指定 */
}
@media (max-width: 600px) {
.grid-item { width: 45%; }
}
const msnry = new Masonry(grid, {
itemSelector: '.grid-item',
percentPosition: true, // %指定の幅を正しく計算する
})
percentPosition: trueを指定すると、widthをピクセルではなく%で指定した要素でも正しく位置計算が行われます。CSSのメディアクエリで.grid-itemの幅を切り替えれば、画面サイズに応じて列数が変わるレスポンシブなグリッドを、JavaScript側の分岐なしで実現できます。
まとめ
Masonryは「高さの異なる要素を隙間なく並べる」という一点に特化したライブラリです。itemSelectorやcolumnWidthといった基本オプションだけで動き始める手軽さがありながら、imagesLoadedとの連携やhorizontalOrder、percentPositionといったオプションで、画像ギャラリーからレスポンシブなカードレイアウトまで幅広いケースに対応できます。
CSSのgridやflexboxだけでは実現しにくいPinterest風のレイアウトを作りたくなったら、まずは今回のサンプルをベースに、自分の持っている画像やカードのデータを流し込んで試してみてください。
