はじめに
表形式で、列の色が交互に変わっているデザインってよくあるじゃないですか。
例えば下図のような感じですね。
今回はこんな感じのデザインを作成しようとした時に考えた事を書いた記事になります。
技術的に役に立つかは微妙ですが、一つの要件に対する思考プロセスとしての備忘録も兼ねています。
要件
自分が実現したかったのは、下記のような要件のレイアウトです。
- 項目数は可変(行数に対してキリがいいとは限らない)
- 行の最大値は固定だが、列は項目数で変動 ただし行数は今後変わる可能性あり
- 1列目が埋まったら次の列、という順番で埋めていく
ちょうど図の番号順に並べられていく感じですね。
これをHTMLとCSSで表現しようとする場合、色々な方法が考えられます。
例えば<table>
を使って組んだり、flexboxを使用するのも一つの手。どちらにしてもこんな感じでしょうか。
<!-- 1列あたり9行とする -->
<div class="table">
<div class="column">
<div class="cell">item1</div>
<div class="cell">item2</div>
<div class="cell">item3</div>
<div class="cell">item4</div>
<div class="cell">item5</div>
<div class="cell">item6</div>
<div class="cell">item7</div>
<div class="cell">item8</div>
<div class="cell">item9</div>
</div>
<div class="column">
<div class="cell">item10</div>
<div class="cell">item11</div>
<div class="cell">item12</div>
<div class="cell">item13</div>
<div class="cell">item14</div>
<div class="cell">item15</div>
<div class="cell">item16</div>
<div class="cell">item17</div>
<div class="cell">item18</div>
</div>
<div class="column">
<div class="cell">item19</div>
<div class="cell">item20</div>
<div class="cell">item21</div>
<!-- 以下略 -->
</div>
.table {
display: flex;
.column {
&:nth-child(odd) .cell {
background-color: white;
}
&:nth-child(even) .cell {
background-color: aliceblue;
}
.cell {
/* セルの高さとかボーダーとかは省略 */
}
}
}
特に難しいものではなく、基礎的なdisplay: flex
や:nth-child
の使い方が分かっていれば大丈夫ですね。
flexboxで列の要素<div class="column">
を横並びにし、その要素が偶数番目か奇数番目かで子のセル要素のbackground-color
を設定しています。
でもデメリットとしては、JavaScriptを使うなり何なりで項目数に合わせて列ごとに要素を分割するとか考えなければならないことがあります。
面倒ですね。それぐらい書けよ
ですがもっとシンプルに並べる方法として、Grid Layoutが存在しています。
これならHTMLは何も考えずに平坦でOKになるはず。つまり項目数に合わせて列を分割して~とか考える必要がない!楽だ!
ってことで考えてみました。
結果
まず、HTMLやCSSがどんな感じになるかを考えてみます。
とりあえず背景色は抜きでやってみましょう。
<!-- 1列あたり9行とする -->
<div class="table">
<div class="cell">item1</div>
<div class="cell">item2</div>
<div class="cell">item3</div>
<div class="cell">item4</div>
<div class="cell">item5</div>
<div class="cell">item6</div>
<div class="cell">item7</div>
<div class="cell">item8</div>
<div class="cell">item9</div>
<div class="cell">item10</div>
<div class="cell">item11</div>
<div class="cell">item12</div>
<div class="cell">item13</div>
<div class="cell">item14</div>
<!-- 以下略 -->
</div>
.table {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-template-rows: repeat(9, 30px);
grid-auto-flow: column;
.cell {
/* 一旦省略 */
}
}
HTMLは大分シンプルにすることができました!
後はGrid Layoutの書き方ですが、列は可変なのでrepeat(auto-fill, 列幅)
で指定することで実現できます。
行については、今回は最大9行なのでrepeat(9, 行の高さ)
で指定します。
そしてgrid-auto-flow: column
でセルを列から埋め込んでいく形とすれば、綺麗に並んでくれますね!
後はそれぞれのセルの背景色設定ですが、これが当時の自分にとって悩みポイントでした。
最大で1列あたり9行なので、:nth-child
で9nを使うのかなーとか考えたのですが上手くいかず。
ただ、よくよく考えると2列ごとに背景色の繰り返しが来るので18nで考えればいいのでは?となってこうなりました。
.table {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-template-rows: repeat(9, 30px);
grid-auto-flow: column;
.cell {
/* ボーダーとかは省略 */
/* 奇数列 */
&:nth-child(18n + 1),
&:nth-child(18n + 2),
&:nth-child(18n + 3),
&:nth-child(18n + 4),
&:nth-child(18n + 5),
&:nth-child(18n + 6),
&:nth-child(18n + 7),
&:nth-child(18n + 8),
&:nth-child(18n + 9) {
background-color: white;
}
/* 偶数列 */
&:nth-child(18n + 10),
&:nth-child(18n + 11),
&:nth-child(18n + 12),
&:nth-child(18n + 13),
&:nth-child(18n + 14),
&:nth-child(18n + 15),
&:nth-child(18n + 16),
&:nth-child(18n + 17),
&:nth-child(18n) {
background-color: aliceblue;
}
}
}
要は18個ずつの区切りで考えると、1~9個目が奇数列・10~18個目が偶数列ということですね。
行数をnとすると、1~n個目が奇数列・n+1~2n個目が偶数列ということです。
整理するとシンプルですが、当時の自分は迷ってしまったので仕方ない。
しかし記述がすごいことになっているので、さすがにこれをそのまま書くのは避けたいですね。
というか行数が変わったときに面倒だし、せめて一つの変数でいろいろ管理できないと…
ということでVue + scssで書き直してみました。
<template>
<!-- itemCont: 項目数を表す変数 -->
<div class="table">
<div
v-for="n in itemCont"
:key="n"
class="cell"
>
{{ 'item' + n }}
</div>
</div>
</template>
<style lang="scss" scoped>
// 行数
$row-count: 9;
.table {
display: grid;
grid-template-columns: repeat(auto-fill, 100px);
grid-template-rows: repeat($row-count, 30px);
grid-auto-flow: column;
.cell {
// ボーダーとかは省略
// 背景色設定
@for $i from 1 through $row-count {
// 奇数列は白
&:nth-child(#{$row-count * 2}n + #{$i}) {
background-color: white;
}
// 偶数列は水色
&:nth-child(#{$row-count * 2}n + #{$row-count + $i}) {
background-color: aliceblue;
}
}
}
}
</style>
だいぶスッキリしました。
scssで行数を$row-count
という変数に入れつつ、それをgrid-template-rows
のrepeat数に使用したり、@for
ループで参照して背景色設定に使用することで$row-count
を変更すればどんな行数にも対応できるようになっています。
HTML部分はVueなのでv-for
でループさせていますが、まあReactでも他でも考え方は一緒ですね。
細かい部分は残っていますが、とりあえず要件を満たしつつ満足できるコードになったので一件落着です。
最後に
Grid Layoutとか書いていますが、最終的にはscssのループと組み合わせたり技術的には若干何がしたかったのやらといった内容になってしまいました。
備忘録的なものなので、何か部分部分でも参考になってくれれば幸いです。
また、今回は単純に自分が引っ掛かった内容について、改めて思考のプロセスというか流れを整理して記事に起こしたのですが、こう見るとそこまで悩む内容でもなかったのでは?と思ってしまいます。
記事にする時は色々と順序立てて整理して書き起こすので、そうするとたどり着くべき結論が早い段階で明確に見えやすくなるんでしょうね。(結論を決めているというのもありますが)
普段からコーディングで考える時も、今回の思考プロセスを参考にしつつ活かしていきたいですね。