0
0

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 5 years have passed since last update.

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

0
Posted at

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

■解決したい課題

文章構造を意識して<dt><dd>でタブ切り替えしたい

ざっくり仕様

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

Windowサイズ取得

nuxt.jsで現在のウインドウサイズをどのコンポーネントからでもリアクティブなオブジェクトとして取得できるプラグインのつくりかた
こちらのコードを拝借した。
とてもわかりやすく、すんなり動作した。

TabsResponsive.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版で高さを取得するスクリプトを書く。
ポイントはmethodstabs()で、展開したコンテンツの高さと
トップからの表示位置を計算している。
これをやならければposition:absolute;による表示位置の調整が
できなかった(わたしは、ね)。

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のデザインによってはなんか微妙なところもあるけれど、
いろんなサイトを参考にはしたものの、望んていたものができた、
という時間があって嬉しかった。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?