2018年03月22日追記
アコーディオンひとつ分をコンポーネント化して、コンポーネント内だけで状態管理する方が良いので書き直しました。
vue.js 2.x その0004-02 アコーディオンを作る(jQueryのslideToggleみたいなものを作る) - Motomichi Works Blog
サンプルコード(v-forで出力した要素にそれぞれ)
v-forで出力した要素それぞれにアコーディオンを入れるイメージで、以下のように書いてみました。
<style>
.accordion{}
.accordion__trigger{}
.accordion__contents-outer-wrap{
transition: max-height 2s;
max-height: 0;
overflow: hidden;
background: #eeeeff;
}
.accordion__contents-inner-wrap{
padding: 100px 10px;
}
</style>
<div id="accordion-app">
<ul>
<li class="accordion" v-for="(item, index) in items">
<button class="accordion__trigger" v-on:click="slideToggle(index)">
accordion-trigger-{{index}}
</button>
<div class="accordion__contents-outer-wrap" v-bind:style="{ maxHeight: items[index].contents.maxHeight + 'px' }">
<div class="accordion__contents-inner-wrap" ref="contentsInner">
{{item.title}}<br>
{{item.contents.lead}}
</div>
</div>
</li>
</ul>
</div>
<script src="js/vue.js"></script>
<script>
var items = [
{
title: 'content-0',
contents: {
lead: 'lead-0',
isActive: false,
maxHeight: 0,
}
},
{
title: 'content-0',
contents: {
lead: 'lead-0',
isActive: false,
maxHeight: 0,
}
},
];
var vueApp = new Vue({
el: '#accordion-app',
data: {
items: items,
},
methods: {
slideToggle: function(index){
this.items[index].contents.isActive = !this.items[index].contents.isActive;
if(this.items[index].contents.isActive){
this.items[index].contents.maxHeight = this.$refs.contentsInner[index].clientHeight;
}else{
this.items[index].contents.maxHeight = 0;
}
},
},
});
</script>
trタグの開閉も書いてみましたがHTMLタグが多くなりますね。
display: table-rowでも同じですね。
<style>
.container{
width: 600px;
}
table{
width: 100%;
border-collapse: collapse;
border-spacing: 0;
}
.contents-wrap{
padding: 10px;
background: #eeeeff;
}
tr + tr .contents-wrap{
border-top: 1px solid #aaaaaa;
}
th,td{
border: 0;
padding: 0;
}
.accordion{
transition: max-height 2s;
max-height: 0;
overflow: hidden;
}
</style>
<div id="vue-app">
<div class="container">
<table>
<tr>
<th>
<div>
<div class="contents-wrap">
ラジオボタンrow
</div>
</div>
</th>
<td>
<div>
<div class="contents-wrap">
<span class="controll">
<input type="radio" name="hoge-radio" value="true" v-model="hoge">
<label for="">開く</label>
</span>
<span class="controll">
<input type="radio" name="hoge-radio" value="false" v-model="hoge">
<label for="">閉じる</label>
</span>
</div>
</div>
</td>
</tr>
<tr>
<th>
<div class="accordion" :style="{'max-height': state.hoge.maxHeight + 'px'}">
<div class="contents-wrap" ref="hoge">
トグルrow
</div>
</div>
</th>
<td>
<div class="accordion" :style="{'max-height': state.hoge.maxHeight + 'px'}">
<div class="contents-wrap">
トグルコンテンツ
</div>
</div>
</td>
</tr>
<tr>
<th>
<div>
<div class="contents-wrap">
ラジオボタンrow
</div>
</div>
</th>
<td>
<div>
<div class="contents-wrap">
<span class="controll">
<input type="radio" name="foo-radio" value="true" v-model="foo">
<label for="">開く</label>
</span>
<span class="controll">
<input type="radio" name="foo-radio" value="false" v-model="foo">
<label for="">閉じる</label>
</span>
</div>
</div>
</td>
</tr>
<tr>
<th>
<div class="accordion" :style="{'max-height': state.foo.maxHeight + 'px'}">
<div class="contents-wrap" ref="foo">
トグルrow
</div>
</div>
</th>
<td>
<div class="accordion" :style="{'max-height': state.foo.maxHeight + 'px'}">
<div class="contents-wrap">
トグルコンテンツ
</div>
</div>
</td>
</tr>
</table>
</div>
</div>
<script src="js/vue.js"></script>
<script>
var vueApp = new Vue({
el: '#vue-app',
data: {
hoge: 'false',
foo: 'false',
piyo: 'false',
state: {
hoge: {
isVisible: false,
maxHeight: 0,
},
foo: {
isVisible: false,
maxHeight: 0,
},
}
},
watch: {
hoge: function(val){
this.slideToggle('hoge', val);
},
foo: function(val){
this.slideToggle('foo', val);
},
},
methods: {
slideToggle: function(key, val){
this.state[key].isVisible = !this.state[key].isVisible;
if(this.state[key].isVisible){
this.state[key].maxHeight = this.$refs[key].clientHeight;
}else{
this.state[key].maxHeight = 0;
}
console.log(this.state[key]);
},
},
});
</script>