こんにちは、@na4daです。
今回のテーマはアコーディオンメニューの実装です。
アコーディオンメニューの実装方法を調べると、CSSとjQueryを使うやり方がたくさん出てくるのですが、CSSだけでも作れるという記事をいくつか見かけました。そこで、ClojuresScriptとcssを組み合わせて、アコーディオンメニューを実装をしてみようと思います。
用意
- ClojureScript (Reagent)
- Sass (CSS)
今回は、タイトルの通りReagentで実装を行います。また、スタイルの追加は、SCSSファイルとsass4cljを用いてCSSファイルの生成を行います。
作成したコードはこちらに置いてあります。これ以降は、こちらのコードを取り上げながら説明していきます。内容は、九九の各段をアコーディオンメニューで簡単に実装したものになります。
ClojureScript側
アコーディオンメニューの核となる部分は以下のようになっています。
(defn accordion-list [num]
(let [default-idx 3
content-list* (partial content-list num)]
[:div {:id num :key num}
[:input.acc-toggle {:type "checkbox"
:id (str "toggle_" num)
:default-checked (= num default-idx)}]
[:label.acc-label {:for (str "toggle_" num)}
[:span (str num "の段")]]
[:div.acc-content
[:ul
(map content-list* (range 1 10))]]]))
アコーディオンメニューを構成するためには、大きく分けて3つの要素が必要となります。
input
アコーディオンの開閉の切り替えを担当します。
[:input.acc-toggle {:type "checkbox"
:id (str "toggle_" num)
:default-checked (= num default-idx)}]
type
属性には"checkbox"
または"radio"
のいずれかを与えます。checked
になった要素のアコーディオンが展開されるように実装します。
また、"checkbox"
と"radio"
それぞれでアコーディオンの挙動が異なります。大まかな違いとしては
-
"checkbox"
: 複数のアコーディオンを展開できる -
"radio"
: アコーディオンは常に一つだけ展開される
となります。
"checkbox"
の場合は、任意のアコーディオンを展開したまま、別のアコーディオンを開くことができます。"radio"
の場合は、別のアコーディオンを展開する際、すでに開いてるアコーディオンは閉じられます。そのため、アコーディオンは常に一つしか展開されません。
:id
には、ユニークな名前を与えます。こちらは後で登場するfor
と紐付けされます。
:default-checked
は、条件を満たす要素をあらかじめchecked
の状態にすることができます。例えば、ページを開いた際にあらかじめ特定のアコーディオンを展開しておきたい場合などに便利です。
label
アコーディオンが閉じた状態の表示部分を担当します。
[:label.acc-label {:for (str "toggle_" num)}
[:span (str num "の段")]]
label
要素のfor
属性には、上記のinput
のid
属性と同じクラス名を与えるようにします。for
属性は、任意のクラス名を持つ要素と関連づけることができます。これを利用して、label
で囲まれた要素をクリックすることでinput
のchecked/unchecked
を切り替えられるようにします。
label
要素の内側には、アコーディオンが閉じた状態で表示する要素を与えます。今回はspan
要素で1行分の内容しか記述していませんが、複数行の内容も入れられます。
:div.acc-content
アコーディオンを展開した際に表示される内容を担当します。
[:div.acc-content
[:ul
(map #(content-list num %) (range 1 10))]]
こちらのdiv
以下には、任意の内容を与えることができます。今回はリストを表示させていますが、table
要素で表を与えたりすることなども可能です。
CSS側 (SCSS)
SCSSは以下のようになっています。
$border: #e6e6e6;
$selected: #C1DEFF;
$hovering: #A5C2FF;
$label-background: #89A7E8;
$content-background: #FDF7E8;
.accbox {
max-width: 450pt;
div:first-child {
.acc-label {
border-top: 1px solid $border;
}
}
}
.acc-toggle {
display: none;
&:checked {
~ .acc-label {
background-color: $selected;
&:hover {
background: $hovering;
}
}
~ .acc-content {
height: auto;
padding: 10px;
}
}
}
.acc-label {
display: block;
height: 30px;
background-color: $label-background;
border-bottom: 1px solid $border;
cursor: pointer;
transition: all 0.1s;
&:hover {
background: $hovering;
}
span {
padding: 5px 10px 0px 10px;
display: inline-block;
text-align: left;
width: 100%;
}
}
.acc-content {
height: 0;
overflow-y: hidden;
transition: all 0.2s;
margin: 0pt;
background-color: $content-background;
ul {
list-style-type: none;
margin: 0pt;
}
}
アコーディオンメニューに最低限必要なスタイルは、以下の3つになります。
.acc-content
.acc-content {
height: 0;
overflow-y: hidden;
transition: all 0.2s;
margin: 0pt;
background-color: $content-background;
ul {
list-style-type: none;
margin: 0pt;
}
}
ここでは、アコーディオンを展開した時の内容は、あらかじめ見えないようにしておくことがポイントです。そのため、height
、overflow-y
に非表示向けのスタイルを与えています。
また、transition
プロパティは、アコーディオンの開閉時に内容の表示/非表示
が切り替わる時間を与えています。小さい値ほど、素早く切り替わります。
.acc-label
.acc-label {
display: block;
height: 30px;
background-color: $label-background;
border-bottom: 1px solid $border;
cursor: pointer;
transition: all 0.1s;
&:hover {
background: $hovering;
}
span {
padding: 5px 10px 0px 10px;
display: inline-block;
text-align: left;
width: 100%;
}
}
この部分に関しては、内容に応じて適宜スタイルを与えます。例えば、cursor
プロパティにpointer
を与えたり、マウスオーバーした際のスタイルを与える等があります。
.acc-toggle
.acc-toggle {
display: none;
&:checked {
~ .acc-label {
background-color: $selected;
&:hover {
background: $hovering;
}
}
~ .acc-content {
height: auto;
padding: 10px;
}
}
}
まず、input
要素は表示させないようにするため、display: none;
を与えます。
その下では、.acc-toggle
クラスに対して擬似クラス:checked
を与え、その中では間接セレクタ~
を用いてスタイルを与えます。この組み合わせによって、input
要素がchecked
になった際に.acc-toggle
クラスと同じ階層に位置する.acc-label
クラスや.acc-content
クラスにスタイルを与えることができます。
この2つのクラスのうち、最低限必要なのは.acc-content
クラスで、デフォルトで非表示だった中身を表示させるスタイルを与えています。
ざっくりとしたまとめ
- 基本的な仕組みは
input
のchecked/unchecked
-
label
のfor
とinput
のid
を対応づける - アコーディオンの開閉はCSSで与える
- デフォルトは非表示にする
-
input
がchecked
の場合に中身を表示させる
今回はReagentとCSSでアコーディオンメニューの実装を行いました。
ReagentのHiccup-likeな記法と、SCSSの入れ子構造が相まって、全体的にとてもコンパクトに記述できた気がします。今回の例では、スタイルの付加は極力少なめにしていますが、さらに調整を加えることでよりオシャレなアコーディオンメニューが作れると思います。
少しでも参考になりましたら、幸いです。