2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Nuxt.js】アコーディオン(スライドトグル)を実装する

Posted at

#■目的
勉強のためNuxt.jsのサンプル集を作る

##目標4
アコーディオン(スライドトグル)を実装する

##ざっくり仕様

  • テンプレート内でのベタ書きパタンと配列読み込みの2パタンを実装する
  • 上記2パタンは1つのコンポネントファイルで動作させる
  • コンテンツ開閉時はアニメーションさせる
  • コンテンツの高さに依存しない

##default.vue
ベースファイルはここで書いたものと同じ。
components/template/Accordion.vue内で処理をしていく。

##Accordion.vue(親)
まずはtemplate部分

components/template/Accordion.vue
<template>
  <main>
    <section id="p-accordion_box02">
      <div class="c-box">
        <!--
          ベタ書きパタン
          dt,ddの関係にしたければコンポネントをdlで括ればいい。
          aco-title-tagで指定するHTMLが開閉用のボタンになる
        -->
        <dl>
          <MoleculesEtcAccordion
            aco-title-tag="dt"
            aco-title="べたっと書く"
          >
            <dd>ここでは開閉ボタンをdt、展開要素をddに指定。この組み合わせが一番好きです。</dd>
          </MoleculesEtcAccordion>
        </dl>
        <MoleculesEtcAccordion
          aco-title-tag="h4"
          aco-title="ベタ書きだから自由"
        >
          <p>次は開閉ボタンをh4、展開要素を複数のタグで構成。</p>
          <p>たとえば画像を設置したり。class指定すればとにかく自由</p>
          <figure><img src="/images/sample/accordion.jpg" alt="アコーディオン" width="960" height="960"></figure>
        </MoleculesEtcAccordion>
      </div>
    </section><!-- /box02 -->

    <section id="p-accordion_box03">
      <div class="c-box">
        <!--
          配列読み込みパタン
          配列で処理するのだから自ずと流し込む型が決まってくる
        -->
        <ul>
          <li v-for="acoList in acoLists" :key="acoList.id" :class="acoList.id">
            <dl>
              <MoleculesEtcAccordion
                aco-title-tag="dt"
                :aco-title="acoList.acoArrayTitle"
                :aco-index="acoList.index"
              >
                <dd>{{ acoList.acoArrayBody }}</dd>
              </MoleculesEtcAccordion>
            </dl>
          </li>
        </ul>
      </div>
    </section><!-- /box03 -->
  </main>
</template>

次にscript部分。
とはいえ、配列を書いているだけです。

components/template/Accordion.vue
<script>
export default {
  computed: {
    acoLists () {
      const acoArrayList = [
        {
          id: 'aco1',
          acoArrayTitle: '配列から読み込む',
          acoArrayBody: '自由記述の代わりに配列から読み込む。マークアップはul>liでliを繰り返している。'
        },
        {
          id: 'aco2',
          acoArrayTitle: 'ただし……?',
          acoArrayBody: '配列で型にはめるからある程度制限されてしまう? 展開するコンテンツ内で自由に記述するにはどうすればいいんだろう。'
        },
        {
          id: 'aco3',
          acoArrayTitle: 'ちなみに',
          acoArrayBody: 'アコーディオンのコンポネントでは機能のみを設定しているので、見た目は読み込むページで自由に変えられる。'
        }
      ]
      return acoArrayList
    }
  }
}
</script>

どうでもいいけれど、配列内のidacoXを指定して、それをそれぞれを
包括する要素のclassに指定している。これにより、cssで可能な範囲内で
自由度を表現するバックアップ的な指定で、本来は必要ない。

##Accordion.vue(子)
親で読み込んでいるコンポネントのtemplate部分
なにも複雑なことはなく、@clickv-ifを必要な箇所に
記述しているだけ。

components/molecules/etc/Accordion.vue
<template>
  <div class="c-accordion">
    <!--
      おなじみcomponentタグでacoTitleTagをpropsで受け取り
      親で指定したタグでボタン部分を括る
    -->
    <component
      :is="acoTitleTag"
    >
      <button class="c-accordion_btn" type="button" :class="{ 'active' : isOpen }" @click="acoToggle">
        <span>{{ acoTitle }}</span>
        <AtomsIconsArrowIcn />
      </button>
    </component>
    <!--
      アコーディオン部分をアニメーションさせるお作法transition
    -->
    <transition
      name="accordion"
      @before-enter="beforeEnter"
      @enter="enter"
      @before-leave="beforeLeave"
      @leave="leave"
    >
      <div v-if="isOpen" class="c-accordion_body" :class="{ 'active' : isOpen }">
        <div class="c-accordion_body--inner">
          <slot />
        </div>
      </div>
    </transition>
  </div>
</template>

script部分もシンプル。

components/molecules/etc/Accordion.vue
<script>
export default {
  props: { // propsで親からデータを受け取る
    acoTitleTag: {
      type: String,
      default: ''
    },
    acoTitle: {
      type: String,
      default: ''
    }
  },
  data () {
    return { // 開閉フラグの初期値をfalseに
      isOpen: false
    }
  },
  methods: {
    acoToggle () { // @click="acoToggle"でクリックする度に開閉フラグを逆の値に
      this.isOpen = !this.isOpen
    },
    beforeEnter (el) { // leave(el)まではアコーディオンアニメーションのお作法で
      el.style.height = '0'
    },
    enter (el) {
      el.style.height = el.scrollHeight + 'px'
    },
    beforeLeave (el) {
      el.style.height = el.scrollHeight + 'px'
    },
    leave (el) {
      el.style.height = '0'
    }
  }
}
</script>

##結果
求めたいたアコーディオン機能が実装できた。
コンポネント分けすることで、1つクリックするとすべてのアコーディオンが開く、
といったようなこともない。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?