ハローワールド。
Masonry, magic grid, etc...。みなさんはこのライブラリを知っていますか?
これらは、グリッドレイアウトを実現するためのライブラリです。
通常、CSSのflexboxやgridを使えばグリッドレイアウトは可能ですが、これらのライブラリは高さの違うコンテンツを"いい感じに"表示してくれます。
例えば、Magic Gridであれば以下のようにフレキシブルなグリッドレイアウトを実現してくれます。(以下のGifはMagic GridのGithubのReadmeより引用)
Flexboxでグリッドレイアウトを作ると高さが一定になってしまうため妙な余白ができてしまいます。
かといってGridを利用するときには事前に高さとか配置とか決めておく必要があるので動的に表示するのはなあ...ということで、こういったライブラリを利用していました。
しかし、これらのライブラリもメンテナンスがされてなかったり、TypeScriptのtypeがなかったり、実装を見るとleftとtopやtransformで座標を指定したりと手放しで喜んで利用したい、というライブラリではありませんでした(個人の感想です)。
なので、今回ライブラリ無しで、できる限りCSSだけでこれを再現したかったので投稿いたしました。
実際にはCSSだけではできませんでしたが。
もっと簡単にできるなら教えて下さい(切実)
完成品
See the Pen Card Grid Layout without Library by sa2taka (@sa2taka) on CodePen.
説明
上記の動作はCSSのGridを利用して、ほぼ無理やり実現しています。
css
cssでは大したことはやってないのであまり詳しい説明はいらないかもしれません(jsもそうですが)
#grid
まず最初に上記のhtmlでは
<div id="grid">
</div>
となっている部分です。このgridは以下のスタイルが適用されています。
# grid {
display: grid;
grid-auto-rows: 20px;
grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
}
display: grid
の説明はいいとして、grid-template-columns: repeat(auto-fit, minmax(200px, 1fr))
ですが、詳細はMDNに任せるとして(grid-template-columns, repeat)、結果的に言えばウィンドウの横幅に合わせて列数を"いい感じ"にフィットさせてくれるやつです。これのおかげで随分フレキシブルな動きが可能です。
今回重要となるのがgrid-auto-rows
です。MDNによると、
grid-auto-rows と grid-auto-columns プロパティで、暗黙的なグリッドに作成されたトラックのセットサイズを定義することもできます
ということです。明示的、暗黙的と表現されていますが、動的なコンテンツに対するサイズ、今回はrowsなので縦のサイズを定義するという意味と捉えています。
補足程度ですが、grid-template-rows
でもいいです。若干違いはありますが、だいたいおんなじ見た目になります。もしかしたらtemplateの方が良いのかもしれません。
.item
次にitemクラスですが、こちらは
.item {
min-width: 80px;
margin-left: 8px;
margin-top: 8px;
box-sizing: border-box;
background: green;
color: white;
}
と、大したことはやってないので説明は割愛します。
ちなみに、box-sizing: border-box;
を指定しておかないと予想通りの表示にならなかったりします。多分。
JavaScript
以下にJavaScriptの全体のコードを載せますが、必要なのはaddItem
だけです。
let index = 1;
const addItem = () => {
const height = randomNum();
const grid = document.querySelector('#grid');
const item = document.createElement('div');
item.setAttribute('class', 'item');
item.setAttribute('style', `height: ${height}px;grid-row: span ${Math.ceil(height / 20)};`);
item.innerHTML = index;
index += 1;
grid.appendChild(item);
}
const randomNum = () => {
const min = 40;
const max = 200;
return Math.floor(Math.random() * (max - min + 1) + min)
}
window.addEventListener('load', () => {
for (let i = 0; i < 10; i++) {
addItem();
}
document.querySelector('#addItemButton').addEventListener('click', () => {
addItem();
})
})
addItem
以下にaddItem部分だけ載せます
const addItem = () => {
const height = randomNum();
const grid = document.querySelector('#grid');
const item = document.createElement('div');
item.setAttribute('class', 'item');
item.setAttribute('style', `height: ${height}px;grid-row: span ${Math.ceil(height / 20)};`);
item.innerHTML = index;
index += 1;
grid.appendChild(item);
}
単純に#grid
にdiv.item
を追加するだけのコードですが、重要なのはここです。
item.setAttribute('style', `height: ${height}px;grid-row: span ${Math.ceil(height / 20)};`);
style
要素に高さとgrid-row
を書き込んでいます。heightはいいとして、grid-row
は何でしょうか。
MDNによれば、gridのアイテムの実際の高さを決める項目です。grid-row
は実際には絶対的に数字を入れる必要がありますが、span
を入れることで指定した値分の高さを埋めるように自動的に位置が決まります。
例えばgrid-row: 3
とした場合はrowが3から始まりますが、grid-row: span 3
とした場合はgridの高さが3になるように自動的に入り込みます。ちなみにこの高さの単位は、先程指定したgrid-auto-rows
の値になりますので、span 3
であれば60pxになるようにGridが埋まります。
つまり、ここをgrid-row: span 高さ / grid-auto-rowsで指定した値
とすれば高さを"いい感じに"指定しつつ、配置もCSSのgridの力を借りて"いい感じに"してくれるようになります。
言葉だけでは理解しづらいかもしれませんが、ChromeのDeveloper ToolsのElementsから確認すると案外わかりやすいです。
CSSのgrid-auto-rows
で20pxごとにラインを引いていきます。それが紫のラインですね。そして、grid-row
で高さを決めることで、gridの強さを活かしつつ高さも"いい感じに"表示できるって感じですね。
ただ、残念ながらウィンドウ幅を動かしたときに、Magic-Gridのようにカラムが動くようなアニメーションは付きません。悪しからず。
最後に
今回の説明ではMDNを利用しつつ軽く説明していますが、私もGrid初心者みたいな存在なので、解釈違いなどあるかもしれませんし、実はGridをグリグリ動かせばjavascriptさえ使わずに実現できるかもしれません。magic-grid使ったほうが良くね
急ぎ足で解説しましたので乱文ですが、以上となります。
ツッコミ、"こんな方法あるよ"、"このライブラリ便利だよ"等あったらお願いします。