Help us understand the problem. What is going on with this article?

Reagentによるアコーディオンメニューの実装

こんにちは、@na4daです。

今回のテーマはアコーディオンメニューの実装です。

アコーディオンメニューの実装方法を調べると、CSSとjQueryを使うやり方がたくさん出てくるのですが、CSSだけでも作れるという記事をいくつか見かけました。そこで、ClojuresScriptとcssを組み合わせて、アコーディオンメニューを実装をしてみようと思います。

用意

  • ClojureScript (Reagent)
  • Sass (CSS)

今回は、タイトルの通りReagentで実装を行います。また、スタイルの追加は、SCSSファイルとsass4cljを用いてCSSファイルの生成を行います。

作成したコードはこちらに置いてあります。これ以降は、こちらのコードを取り上げながら説明していきます。内容は、九九の各段をアコーディオンメニューで簡単に実装したものになります。

accordion-menu.gif

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属性には、上記のinputid属性と同じクラス名を与えるようにします。for属性は、任意のクラス名を持つ要素と関連づけることができます。これを利用して、labelで囲まれた要素をクリックすることでinputchecked/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;
  }
}

ここでは、アコーディオンを展開した時の内容は、あらかじめ見えないようにしておくことがポイントです。そのため、heightoverflow-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クラスで、デフォルトで非表示だった中身を表示させるスタイルを与えています。

ざっくりとしたまとめ

  • 基本的な仕組みはinputchecked/unchecked
  • labelforinputidを対応づける
  • アコーディオンの開閉はCSSで与える
    • デフォルトは非表示にする
    • inputcheckedの場合に中身を表示させる

今回はReagentとCSSでアコーディオンメニューの実装を行いました。

ReagentのHiccup-likeな記法と、SCSSの入れ子構造が相まって、全体的にとてもコンパクトに記述できた気がします。今回の例では、スタイルの付加は極力少なめにしていますが、さらに調整を加えることでよりオシャレなアコーディオンメニューが作れると思います。

少しでも参考になりましたら、幸いです。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした