【Nuxt.js】タブ切り替えを実装する2

[【Nuxt.js】タブ切り替えを実装する](https://qiita.com/MasayukiAbe/items/b65f565d20f7ea28144e "【Nuxt.js】タブ切り替えを実装する")
の続き

#■解決したい課題
**文章構造を意識して`<dt>`と`<dd>`でタブ切り替えしたい**

##ざっくり仕様
* タブのボタン部分もコンテンツ部分も同じ配列から読み込む
* タブのボタンをコンテンツ部分のHTMLを指定できれば`<dt><dd>`だろうが`<hx><p>`だろうがなんでもOK
* PCではタブ切り替え、SPではアコーディオンにする

##Windowサイズ取得
[nuxt.jsで現在のウインドウサイズをどのコンポーネントからでもリアクティブなオブジェクトとして取得できるプラグインのつくりかた](https://qiita.com/allein-s/items/967a525e862a913bc3b1 "nuxt.jsで現在のウインドウサイズをどのコンポーネントからでもリアクティブなオブジェクトとして取得できるプラグインのつくりかた")
こちらのコードを拝借した。
とてもわかりやすく、すんなり動作した。

##TabsResponsive.vue

```vue:components/molecules/etc/TabsResponsive.vue
<template>
  <div ref="tabs" class="c-tab-responsive">
    <component :is="tabTag" ref="tabBtn" class="c-tab-responsive_menu">
      {{ tabName }}
    </component>
    <transition
      name="accordion"
      @before-enter="beforeEnter"
      @enter="enter"
      @before-leave="beforeLeave"
      @leave="leave"
    >
      <component :is="tabBodyTag" v-show="isActive === tabId" ref="tabBody" class="c-tab-responsive_content">
        <div class="c-tab-responsive_content--block">
          <div class="c-tab-responsive_content--block-inner">
            <div v-html="tabText" />
          </div>
        </div>
      </component>
    </transition>
  </div>
</template>

<script>
export default {
  props: {
    isActive: {
      type: Number,
      default: 1
    },
    tabId: {
      type: Number,
      default: 1
    },
    tabTag: {
      type: String,
      default: ''
    },
    tabName: {
      type: String,
      default: ''
    },
    tabBodyTag: {
      type: String,
      default: ''
    },
    tabText: {
      type: String,
      default: ''
    }
  },
  mounted () {
    this.tabs()
  },
  methods: {
    beforeEnter (el) {
      if (this.$window.width < 1024) {
        el.style.height = '0'
      }
    },
    enter (el) {
      if (this.$window.width < 1024) {
        el.style.height = el.scrollHeight + 'px'
      }
    },
    beforeLeave (el) {
      if (this.$window.width < 1024) {
        el.style.height = el.scrollHeight + 'px'
      }
    },
    leave (el) {
      if (this.$window.width < 1024) {
        el.style.height = '0'
      }
    },
    tabs () {
      if (this.$window.width >= 1024) {
        const tabs = this.$refs.tabs
        const tabBtn = this.$refs.tabBtn
        const tabBody = this.$refs.tabBody
        const tabBtnHeight = tabBtn.clientHeight
        const tabBodyHeight = tabBody.clientHeight
        const tabsHeight = tabBtnHeight + tabBodyHeight
        tabs.style.height = tabsHeight + 'px'
        tabBody.style.top = tabBtnHeight + 'px'
      }
    }
  }
}
</script>
```

`<script>`はだらだらと書いていてとても恥ずかしいのだけど、
いつかきっともっとうまく書けるように勉強し続ける。
実際のコンテンツはすべて親コンポネント側で管理するので、
こちらではとにかく受け取る準備と、PC版で高さを取得するスクリプトを書く。
ポイントは`methods`の`tabs()`で、展開したコンテンツの高さと
トップからの表示位置を計算している。
これをやならければ` position:absolute; `による表示位置の調整が
できなかった（わたしは、ね）。

```vue:components/molecules/etc/TabsResponsive.vue
<template>
  <main>
    <section id="p-tabs-menu_box03">
      <div class="c-box">
        <ul class="p-tabs-menu_box03-tablist">
          <li v-for="tab2 in tabs2" :key="tab2.tabId" :class="[ activetab2 === tab2.tabId ? 'active' : '', tab2.tabClass ]" class="p-tabs-menu_box03-tab" @click="changeTab(tab2.tabId)">
            <dl>
              <MoleculesEtcTabsResponsive
                :is-active="activetab2"
                :tab-id="tab2.tabId"
                :tab-name="tab2.tabName"
                :tab-tag="tab2.tabTag"
                :tab-body-tag="tab2.tabBodyTag"
                :tab-text="tab2.text"
              />
            </dl>
          </li>
        </ul>
      </div>
    </section><!-- /box03 -->
  </main>
</template>

<script>
export default {
  data () {
    return {
      activetab2: 1,
      tabsHeight: 0,

      tabs2: [
        {
          tabStatus: null,
          tabId: 1,
          tabClass: 'c-tab_menu--item02-01',
          tabName: 'タブ02-01',
          tabTag: 'dt',
          tabBodyTag: 'dd',
          text: '<p>タブ2のテスト</p>' +
                '<p>1つの配列の中でタブのテキストと展開コンテンツを管理。</p>' +
                '<p>要素の表示/非表示はcssで管理している。これでうまくいくのか。</p>'
        },
        {
          tabStatus: null,
          tabId: 2,
          tabClass: 'c-tab_menu--item02-02',
          tabName: 'タブ02-02',
          tabTag: 'dt',
          tabBodyTag: 'dd',
          text: '<p>変わらずv-htmlを採用しているので中のHTMLは自由</p>' +
                '<p>ただ、<strong>iframeなどは検証していないのでどうなるかわからない。</strong></p>'
        },
        {
          tabStatus: null,
          tabId: 3,
          tabClass: 'c-tab_menu--item02-03',
          tabName: 'タブ02-03',
          tabTag: 'dt',
          tabBodyTag: 'dd',
          text: '<p>タブ2のテスト3</p>'
        }
      ]
    }
  },
  methods: {
    changeTab (tabId) {
      this.activetab2 = tabId
    }
  }
}
</script>
```

親コンポネント。
配列を指定して、すべてそこから情報を読み込んでいる。
ただ読み込んでいるだけで目新しいことはなにもなし。

##結果
うん、動いた。cssのデザインによってはなんか微妙なところもあるけれど、
いろんなサイトを参考にはしたものの、望んていたものができた、
という時間があって嬉しかった。