javascriptのmasonryとcssのcolumns

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
タイル

この記事を三行にまとめると

CSSだけでも似たような動きを実装できることを知りました
宝くじを30円分買ったら10万円が当たったかのようなお得感です
完全にjQueryが不要になりました
Masonryという、高さの違うブロック要素をタイル状に並べてくれるjQueryのプラグインがあります。画面のリサイズ時にも列ごとの高さが均等になるようにブロック要素を動的に入れ替えたりもしてくれます。僕もこのブログのトップページとかに使っていました。

実際に動きを見るとこんな感じです。

masonry

画像が荒くて申し訳ないですがブロック要素が入れ替わったりしているのが分かると思います。

高さの異なるブロック要素を並べた時に隙間を埋めてくれるのでなかなか便利だなーなんて思ってたんですが、最近じゃCSSだけでも似たような動きを実装できることを知りました。

今日はそれをちょっと紹介したいと思います。



columns

それはCSSのcolumnsという機能です。
https://developer.mozilla.org/ja/docs/Web/CSS/columns

Masonryと全く同じ動きにすることは難しいんですが、でもほんの数行でほぼ同じ見た目を実装できると考えると使い勝手はかなり良いんじゃないかと思います。現在はあかつきのお宿もMasonryの代わりにcolumnsを取り入れているのですが、例えばこのブログのトップページみたいにブロック要素を2列に並べたい場合はこんな風に書きます。

//HTML
<div class="container">
  <div>
    <!-- ブロック要素 -->
  </div>
  <div>
    <!-- ブロック要素 -->
  </div>
  <div>
    <!-- ブロック要素 -->
  </div>
  <div>
    <!-- ブロック要素 -->
  </div>
</div>

//CSS
.container {
  column-count: 2;
  column-gap: 15px;
}

たったこれだけでOKです。javascriptを読み込む必要はない。

「column-count」というのは列数です。2列にしたければ2にすれば良い。3にすれば3列になります。列じゃなくて段って言い方が正しいのかな?

「column-gap」はブロック要素同士の間隔です。marginに近い感覚だと思いますが、自分でブロック要素にmarginを当てるよりはこっちで指定する方が楽だと思います。margin-leftとかmargin-rightを細かく指定する必要がないですからね。

個人的にMasonryよりcolumnsの方が良いなと思う点は大きく分けて2つあります。



コードが激減する

まずは何と言ってもこれにつきますね。先ほども見てもらったようにCSSを4行しか書いてないですからね。columnsの指定に関する部分だけで言えば実質2行。文字にして約30文字。たったの30バイトです。

これがMasonryだとこれだけのコードが必要になります。長いので外部リンクにしときますね。
Masonryを動かすためのjavascript

実際に自分で書く必要があるのは最後の数行だけなんですけど、Masonryのjsが必要になることと、さらにMasonryを動かすためにはjQueryも必要になるため、合算すると結構なコードになります。サイズにして110KBくらいあります。

10万文字以上あるjavascriptのコードがたった30文字程度に置き換えられるってとんでもないことですよ。10万文字の小説の本文を30文字に削れって言われても不可能ですからね。宝くじを30円分買ったら10万円が当たったかのようなお得感です。



画像の遅延読み込みと相性が良い

例えばこちらの「lazyestload.js」などを使って画像の遅延読み込みを行なう場合、Masonryだと見た目が崩れてしまうことがあります。

画像遅延読み込みによるデメリット

こんな感じで妙な余白(赤枠の部分)が開いてしまうことがある。

どうしてこうなるかって言うと、Masonryというのは各ブロック要素に対して絶対位置を割り当てるような処理を行なってるんですよ。

例えばこれ(↓)を見てください。

ポジションがずらーっと

中のdiv一つ一つに「position:absolute」と「top」「left」の値が入っていますね。画面をリサイズするとMasonryはこのtopやleftの位置を再計算して配置を調整してくれます。

画像を遅延読み込みする場合、画像が読み込まれてない状態でMasonryが作動するので、画像の高さが0の状態で各ブロック要素のtopやleftが計算されてしまったりします。画面をスクロールして画像が表示されただけではMasonryは位置を調整してくれないので、上記のように妙な隙間ができてしまったり、逆にブロック同士が重なったりしてしまうことがあります。

表示される画像の大きさが事前に決まっていれば画像が読み込まれる前の状態でもその高さ分のスペースをあらかじめ確保しておくなどの対応ができますが、レスポンシブだとページを開いている人の環境によって画像の高さも変わったりするので、その辺の計算はちょっとめんどくさい。一応Masonryにはリロードみたいな機能もあるので、画像が表示されるタイミングで位置を再計算させたりはできると思いますが、その処理は自分で書かなきゃいけないのでいずれにしても手間がかかることには変わりない。だから僕もMasonryを使っていた時は遅延読み込みは諦めてました。

その点、columnsは絶対位置で調整するわけではないので、ブロック要素の高さが変われば自動的に位置が再調整されます。したがって画像が表示されれば他のブロックも勝手に動いてくれる。遅延読み込みしている人にとってはこの手軽さは大きいと思います。



デメリットもないわけではない

ここまで来ると「Masonryなんていらんかったんや」と思うかもしれませんが、columnsの方が使い勝手が悪い部分もないわけじゃない。

そのうちの一つが配置される順番です。Masonryとcolumnsではブロック要素の配置順が異なります。順番というか場所って言った方が良いのかな。

例えば今、6つのブロック要素があったとします。

//HTML
<div class="container">
  <div>
    ブロック要素1
  </div>
  <div>
    ブロック要素2
  </div>
  <div>
    ブロック要素3
  </div>
  <div>
    ブロック要素4
  </div>
  <div>
    ブロック要素5
  </div>
  <div>
    ブロック要素6
  </div>
</div>

このブロック要素たちを何列かで表示する場合、MasonryはZ型に要素が配置されるのに対し、columnsはN型に要素が配置されます。

二列の場合だとこう。

2列の場合の並び順

三列以降でも考え方は同じです。したがって「画面の上から順に日付の新しい方からブロック要素を並べたい」みたいな場合はcolumnsだと厳しかったりします。いや、それを計算に入れてHTMLの方で並び順を調整しておけば対応できるけど、画面の横幅に応じて列数を変えるようなレスポンシブな作りにしていると、列数が変わった時にやっぱり並びがあべこべになっちゃう。その辺はcolumnsの悩ましいところです。

うちのブログも記事の一覧のところは日付の新しい順に表示されてるんですけど、まあ日付自体は表示してないし、このブログを見に来た人が記事の並びまで細かくチェックしたいと思うことはないだろうという判断で割りきってます。

それからMasonryは列の合計の高さがなるべく均等になるように頑張ってくれますが、columnsだとそうはいきません。リサイズした時に要素の順番を適宜入れ替えたりもしてくれない。場合によっては列ごとに結構な偏りが出ちゃうこともあります。

うちのブログは記事の詳細ページの下部に関連があるかもしれない記事を数件表示してるんですが、columnsにしたらここの高さが思うように揃わないことが多くて、記事の並び順を変えてみたりしてできる限りの調整はしたんですが、これに関してもある程度は仕方ないと割りきることにしました。

あとcolumnsの場合、なぜかIEだと下に変な余白ができちゃうことがあるんだよね……まあこれに関しては、うちはもうIE非推奨で行くことにしたので別にいっかーくらいの感じです。どうしても対応しなきゃいけないって状況でもない限り、もうIEだけで発生する不具合を修正するだけの簡単じゃないお仕事はやらないことに決めました。






ってな感じでCSSのcolumnsの紹介でしたー。

全てにおいてMasonryよりcoulumnsの方が使い勝手が良いとは限らないので、絶対にこっちを使えとは言えません。でも便利だなーとは思うので比較検討してみるのはありだと思います。

しかし僕の印象だと数年前までたいていの動きはjavascriptに頼らないとどうにもならなかったのに、どんどんCSSだけでできることが増えていくな……アニメーションもいろいろできるようになりましたしね。

実はあかつきのお宿もちょっと前まではほぼ全ての動作をjQueryで実装していました。でも思った以上にCSSで代用できることが分かったので、なんやかんやと書き換えたところ、完全にjQueryが不要になりました。次回はちょっとその辺りについてもお話ししたいと思います。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください