表示件数と高さが変わるブロックの一覧を、画面サイズに合わせてカラム数を変えて表示する方法

レスポンシブなサイトで高さが可変のブロック要素の一覧をレイアウトする際にてこずったので、課題になったこととその解決方をメモしておきます。ニュースサイトなどに良くあるサムネイル画像とタイトルのブロック要素の一覧で、画面サイズに合わせてカラム数が変わる以下のようなレイアウトです。ちなみに、Flexboxを使わないやり方です。nth-child()とclearを使った方法、inline-blockを使った方法、さらに、flexboxを使った方法の3つをまとめてみました。

レイアウトの条件

レイアウトの条件を整理しておきます。

  1. 表示件数が変わる
  2. 表示件数が変わる場合でも同じHTMLとCSSでレイアウトする
  3. ブロックによって高さが異なる
  4. 画面サイズに合わせてカラム数を変える

上の条件だとli要素をfloatしてメディア・クエリで画面ごとに幅を指定すれば簡単にできそうです。ところが、ブロックの高さが変わるのでそう簡単にはいきません。

特定の画面サイズでレイアウトが崩れてしまう

例えば、大きい画面では3カラム、中くらいの画面では2カラム、小さい画面では1カラムにするレイアウトで、li要素をfloatを使ってスタイルすると、特定の画面サイズでレイアウトが崩れてしまいます。しかも、下のイメージのように3カラム表示の際にレイアウトが崩れる部分と2カラム表示の際に崩れる部分が異なります。

3カラム表示

4番目のブロックが2番目のブロックに引っかかってレイアウトが崩れてしまう。

2カラム表示

今度は7番目のブロックが5番目のブロックに引っかかってレイアウトが崩れてしまう。本来意図したレイアウトでは、7番目のブロックが左寄せになります。

HTMLとCSSの例

上のようなレイアウトの崩れは、以下のようなHTMLとCSSの記述で起こります。

HTML

li要素の中にサムネイル画像と文字数の違う記事のタイトルが入っています。

CSS

以下の例では、〜600pxまでの画面サイズでは1カラム、600〜800pxの画面サイズでは2カラム、800px以上の画面サイズでは3カラムで表示するように、メディア・クエリを使って各画面サイズのスタイルでli要素に幅を指定しています。

 

nth-child()セレクタとclearプロパティを使う方法

3カラムと2カラムの表示スタイルを指定しているメディア・クエリ部分に、以下を追加するだけで対応ができます。以下のスタイルを追加することで、各カラムの最初の要素にclear: bothを指定して回り込みを解除しています。

600px以上の画面用のスタイルに追加

600〜800pxでは2カラムで表示しているので、それぞれのカラムの1つ目のブロックになる1、3、5、7番目の要素にnth-child(2n+1)を使ってclear: bothを指定します。

 

800px以上の画面用のスタイルに追加

800px以上の画面では3カラムで表示しているので、それぞれのカラムの1つ目のブロックになる1、4、7番目の要素にnth-child(3n+1)を使ってclear: bothを指定します。そして、600〜800pxの画面様に指定した回り込み解除を無効にするためにnth-child(2n+1)clear: noneを指定します。この際、2と3の公倍数で回り込みの解除を無効にしないようにnth-child(3n+1)の方を後に記述します。

 

メディア・クエリ部分の全CSS

最終的に、メディア・クエリ部分のCSSは以下のようになります。

これで、どの画面サイズでもレイアウトが崩れないようになります。

nth-child()のブラウザサポートについて

nth-child()セレクタは、IE9以上とモダンブラウザではサポート されています。IE8以下で対応が必要な場合は、JavaScriptなどで対応する必要があります。そもそも、IE8ではメディア・クエリもサポートされてないですし、対応する状況はあまりないかもしれませんが。

 

inline-blockを使う方法

inline-blockを使ったやり方のほうがいいかも?

以下のようにli要素の間のスペースを削除しないとレイアウトが崩れてしまいますが、それを除けばinline-blockを使った方法のほうがシンプルでいいかもしれません。(まだブラウザでしっかり検証できてないですが。。。)

以下のように、コメントアウトをするなどしてli要素間のスペースを削除しないとレイアウトが崩れてしまうのでご注意を。

CSSは.block-grid liに指定していたfloat: left;display: inline-block;に入れ替えて、vertical-align: top;を追加しました。

 

inline-blockの隙間を無くす2つの方法

上でコメントアウトで消したli要素間のスペースは、親要素のfont-sizeletter-spacingを使って消すことも可能です。

 

font-sizeを使う場合

以下のように親要素にfont-size: 0;を入れてli要素にfont-size: 16px;を入れることでli要素間の隙間をなくすことができます。この際、font-sizeem%の相対値ではli内の文字が表示されなくなるため、pxなどの絶対値を記述する必要があります。

 

letter-spacingを使う場合

以下のように親要素にletter-spacing: -0.4em;を入れてli要素にletter-spacing: normal;を入れることでli要素間の隙間をなくすことができます。

 

Flexboxならもっと簡単?

Flexbox版も試しに作ってみました。自動でブロックの高さが同じになるので、各ブロックに背景やボーダーを入れたい場合などはFlexboxを使うとめちゃくちゃ楽できそうです。IEのバージョンを気にしなくてすむならFlexbox版が一番いいかもしれませんね。

CSS

親要素になる.block-griddisplay: flex;flex-wrap: wrap;を追加してul要素をFlexboxで表示するように指定します。また、子要素の.block-grid liflex: 0 1 98%;を追加して、子要素の表示方法を指定します。この部分をメディア・クエリで画面幅に合わせて変えていきます。

 

 

 

当サイトの自治会、町内会に関する内容の掲載は「お問い合わせ」ページの専用フォームよりお気軽にお寄せ下さい。

※自治会、町内会の情報掲載は全て無料で提供しております。

お問い合わせ