■解決したい課題
文章構造を意識して<dt>と<dd>でタブ切り替えしたい
ざっくり仕様
- タブのボタン部分もコンテンツ部分も同じ配列から読み込む
- タブのボタンをコンテンツ部分のHTMLを指定できれば
<dt><dd>だろうが<hx><p>だろうがなんでもOK - PCではタブ切り替え、SPではアコーディオンにする
Windowサイズ取得
nuxt.jsで現在のウインドウサイズをどのコンポーネントからでもリアクティブなオブジェクトとして取得できるプラグインのつくりかた
こちらのコードを拝借した。
とてもわかりやすく、すんなり動作した。
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;による表示位置の調整が
できなかった(わたしは、ね)。
<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のデザインによってはなんか微妙なところもあるけれど、
いろんなサイトを参考にはしたものの、望んていたものができた、
という時間があって嬉しかった。