LoginSignup
17
12

More than 5 years have passed since last update.

Vue.jsでもお手軽にアコーディオンを導入したい!

Last updated at Posted at 2018-09-15

動機

Vue.js大好き!ですが、毎回面倒なのがアコーディオンの実装。

他の方が作ったコンポーネントもたくさんあげられているのですが、ページ内に複数のアコーディオンを設置したい場合に、いちいちjs側をいじる必要があるものがほとんどです。(本来そうあるべき、なのかもしれませんが…)
エンジニアさんはjs、デザイナーさんはhtml側などと気軽に分担して書けるのは、Vue.jsの利点でもあるので、一度Vue.jsに読んでおけば複数のアコーディオンもhtml側の編集だけで対応できる実装をしました。

実装

js

var accordion = {
  data: function () {
    return {
      openedAccordions: []
    };
  },
  components: {
    accordion: {
      props: ['opened', 'duration', 'closedHeight'],
      computed: {
        innerHeight: function () {
          return this.$refs.inner.clientHeight;
        },
        maxHeight: function () {
          return this.opened ? this.innerHeight : (this.closedHeight || 0);
        },
        transitionDuration: function () {
          return this.duration || 0.5;
        },
        wrapStyle: function () {
          return {
            maxHeight: this.maxHeight + 'px',
            transitionDuration: this.transitionDuration + 's',
            transitionProperty: 'max-height',
            overflow: 'hidden',
          };
        },
      },
      template: '\
          <div :style="wrapStyle">\
            <div ref="inner">\
              <slot></slot>\
            </div>\
          </div>',
    },
  },
  methods: {
    isOpenedAccordion: function (key) {
      return this.openedAccordions.indexOf(key) !== -1;
    },
    openAccordion: function (key) {
      if (this.isOpenedAccordion(key)) return;
      return this.openedAccordions.push(key);
    },
    closeAccordion: function (key) {
      var newOpened = [];
      for (var i = 0; i < this.openedAccordions.length; i++) {
        if (this.openedAccordions[i] === key) continue;
        newOpened.push(this.openedAccordions[i]);
      };
      this.openedAccordions = newOpened;
    },
    toggleAccordion: function (key, open) {
      open = open || !this.isOpenedAccordion(key);
      return open ? this.openAccordion(key) : this.closeAccordion(key);
    },
  },
};

new Vue({
  el: '#app',
  mixins: [accordion], // mixinで読み込む
});

html

<div id="app">

<button @click="toggleAccordion('hoge')" :class="{opened: isOpenedAccordion('hoge')}">
  <span v-if="isOpenedAccordion('hoge')">とじる</span>
  <span v-else>ひらく</span>
</button>

<accordion :opened="isOpenedAccordion('hoge')">
  じゅげむじゅげむ<br>
  ごこうのすりきれ<br>
  かいじゃりすいぎょのすいぎょうまつうんらいまつふうらいまつ<br>
  くうねるところにすむところ<br>
  やぶらこうじのぶらこうじ<br>
  ぱいぽぱいぽぱいぽのしゅーりんがん<br>
  しゅーりんがんのぐーりんだい<br>
  ぐーりんだいのぽんぽこぴーのぽんぽこなのちょうきゅうめいのちょうすけ
  <button @click="closeAccordion('hoge')">とじる</button>
</accordion>

</div>

こんな感じで書いておくと、 <accordion> タグに囲まれた部分が、ボタンにより閉じたり開いたりできます。
ちなみに、 hoge の部分を変えてアコーディオンを増やしていけば、複数アコーディオンにも対応できます。

プロパティ

速度

<accordion>duration属性を指定すると、アコーディオンの表示の時間を10秒に設定できます。

<accordion duration="10"> // 10秒かけて開く

閉じた時の見えている領域

<accordion>closed-height属性を指定すると、アコーディオンが閉じた時にも見えている領域を作ることができます。
複数の投稿を最初の1つだけ見せて、もっと見るボタンに使ったりもできそうですね。

<accordion closed-height="100"> // 100pxだけ最初から表示

やってみて

  • 普段ES6でばかり書いていますが、今回は他の方がコピペしてそのままブラウザで読んでも使えるような書き方でがんばりました。ES6の構文使えないのつらたん…
  • mixinなので、ファイル分けて export / import してもよいかもしれません。
  • Vue.jsだいすき!
17
12
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
17
12