質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

576閲覧

vue-cliにて、v-forでレンダリングしたアコーディオンを一つ開こうとすると全てが開いてしまう

take-t.t.

総合スコア360

Vue.js

Vue.jsは、Webアプリケーションのインターフェースを構築するためのオープンソースJavaScriptフレームワークです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/07/03 13:22

編集2019/07/03 18:20

###前提・実現したいこと
いつもお世話になっております。
現在下記のコードで作ったコンポーネントを別の.vueファイル内で使おうとしているのですが、タイトル通りの現象が起きてしまい困っています。
→gifファイル(imgurに飛びます。)
###発生している問題・エラーメッセージ

コンポーネントについての公式リファレンスを読む限り、この現象を防ぐにはdata(この場合はshowの真偽値)を別コンポーネントに関数で管理すれば大丈夫だと自分なりに理解しています。
しかしこの場合はv-forで同一コンポーネント内でレンダリングしているのでdataを分けられず、結局タイトルに書いた現象がおきてしまいます。
アコーディオンのコンポーネント内ではなく、使用先の.vueファイル内でv-forする事も考えましたがそれは出来ませんでした。

色々と試行錯誤したものの解決策が見つからず完全に手詰まりになってしまっています。
なにか良い方法はあるのでしょうか、よろしければ何卒お力添えをお願いいたします。

以下がアコーディオンのコードになります。(.vueファイルの中身を分けて記載しています。

該当のソースコード

html

1<div class="accordion" v-for="skill in skills" v-bind:key="skill.id" v-bind:style="{ border: '8px' + ' ' + 'solid' + ' ' + skill.bgColor, backgroundColor: skill.bgColor }"> 2 <div class="label" v-on:click="toggleAccordion" v-bind:style="{ backgroundColor: skill.bgColor }"> 3 <h2>{{ skill.name }}<i class="fa fa-angle-down label-icon" v-bind:class="{ rotate : show }"></i></h2> 4 </div> 5 <transition name="slide"> 6 <div class="box" v-show="show"> 7 <img v-bind:src="require(`../assets/images/${skill.name}.png`)"> 8 <p>{{ skill.description }}</p> 9 </div> 10 </transition> 11</div>

JavaScript

1export default { 2 name: 'AccordionComponent', 3 data() { 4 return { 5 skills: [ 6 {id: 1, name: 'HTML・CSS', bgColor: 'red', 7 description: ` 8 text 9 ` 10 }, 11 {id: 2, name: 'JavaScript', bgColor: 'gold', 12 description: ` 13 text 14 ` 15 }, 16 {id: 3, name: 'Vue.js', bgColor: 'forestgreen', 17 description: ` 18 text 19 ` 20 }, 21 {id: 4, name: 'Sass', bgColor: 'hotpink', 22 description: ` 23 text 24 ` 25 }, 26 {id: 5, name: 'Firebase', bgColor: 'orange', 27 description: ` 28 text 29 ` 30 }, 31 {id: 6, name: 'Git・Github', bgColor: 'black', 32 description: ` 33 text 34 ` 35 }, 36 {id: 7, name: 'webpack', bgColor: 'skyblue', 37 description: ` 38 text 39 ` 40 } 41 ], 42 show: false 43 } 44 }, 45 methods: { 46 toggleAccordion: function() { 47 this.show = !this.show 48 } 49 } 50}

SCSS

1<style lang="scss"> 2 .accordion { 3 width: 400px; 4 .label { 5 height: 30px; 6 margin-bottom: 10px; 7 text-align: center; 8 color: white; 9 cursor: pointer; 10 h2 { 11 font-weight: bold; 12 line-height: 30px; 13 } 14 .label-icon { 15 margin-left: 10px; 16 } 17 } //.label 18 .box { 19 overflow: hidden; 20 background-color: white; 21 width: 100%; 22 display: flex; 23 img { 24 display: block; 25 width: 100px; 26 height: 100px; 27 } 28 } //.box 29 } //.accordion 30 31 // 回転アニメーション 32 .rotate { 33 transform: rotate(180deg); 34 transition: 0.3s; 35 } 36 37 // 開閉アニメーション 38 .slide-enter-active { 39 transition-duration: 0.3s; 40 transition-timing-function: ease-in; 41 } 42 .slide-leave-active { 43 transition-duration: 0.3s; 44 transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 45 } 46 .slide-enter-to, .slide-leave { 47 max-height: 100px; 48 overflow: hidden; 49 } 50 .slide-enter, .slide-leave-to { 51 max-height: 0; 52 overflow: hidden; 53 } 54</style>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

LanHma

2019/07/03 13:30

すいません!どこでv-forされているのでしょうか?
take-t.t.

2019/07/03 14:09

申し訳ありません、肝心なところが抜けてしまっていました。 質問文を修正させていただきました。
guest

回答2

0

クリック時にskillのidを引数に持たせて、showに代入し、比較する方法です。

vue

1<div class="accordion" v-for="skill in skills" v-bind:key="skill.id" v-bind:style="{ border: '8px' + ' ' + 'solid' + ' ' + skill.bgColor, backgroundColor: skill.bgColor }"> 2 <div class="label" v-on:click="toggleAccordion(skill.id)" v-bind:style="{ backgroundColor: skill.bgColor }"> 3 <h2>{{ skill.name }}<i class="fa fa-angle-down label-icon" v-bind:class="{ rotate : show }"></i></h2> 4 </div> 5 <transition name="slide"> 6 <div class="box" v-show="show == skill.id"> 7 <img v-bind:src="require(`../assets/images/${skill.name}.png`)"> 8 <p>{{ skill.description }}</p> 9 </div> 10 </transition> 11</div>

javascript

1methods: { 2 toggleAccordion: function(id) { 3 this.show = id 4 } 5 }

投稿2019/07/04 00:07

LanHma

総合スコア192

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

take-t.t.

2019/07/04 03:53

ご回答ありがとうございます。 v-showには真偽値しか使えないと勝手に思っていたので目からウロコです。 とても勉強になりました。
guest

0

ベストアンサー

アコーディオンの開閉状態を各skillごとに別に管理して、その状態を参照してv-showを切り替える必要があると思います。
例えば以下のようなコードでしょうか

サンプルコード

一応2パターン載せておきます。①のほうがロジックはシンプルになりますが、管理する状態が増えます。
②は管理する状態は減りますが、ロジックは若干複雑になります。

今回の場合は①のほうが読みやすいかもしれません。

①開閉状態を別の連想配列として管理するパターン

js

1<template> 2 <div> 3 <div 4 v-for="skill in skills" 5 class="accordion" 6 v-bind:key="skill.id" 7 v-bind:style="{ border: '8px' + ' ' + 'solid' + ' ' + skill.bgColor, backgroundColor: skill.bgColor }" 8 > 9 <div 10 class="label" 11 v-on:click="toggleAccordion(skill.id)" 12 v-bind:style="{ backgroundColor: skill.bgColor }" 13 > 14 <h2> 15 {{ skill.name }} 16 <i 17 class="fa fa-angle-down label-icon" 18 v-bind:class="{ rotate : show[skill.id] }" 19 ></i> 20 </h2> 21 </div> 22 <transition name="slide"> 23 <div class="box" v-show="show[skill.id]"> 24 <!-- <img v-bind:src="require(`../assets/images/${skill.name}.png`)"> --> 25 <p>{{ skill.description }}</p> 26 </div> 27 </transition> 28 </div> 29 </div> 30</template> 31 32<script> 33export default { 34 name: "HelloWorld", 35 data() { 36 return { 37 skills: [ 38 { 39 id: 1, 40 name: "HTML・CSS", 41 bgColor: "red", 42 description: ` 43 text 44 ` 45 }, 46 { 47 id: 2, 48 name: "JavaScript", 49 bgColor: "gold", 50 description: ` 51 text 52 ` 53 }, 54 { 55 id: 3, 56 name: "Vue.js", 57 bgColor: "forestgreen", 58 description: ` 59 text 60 ` 61 }, 62 { 63 id: 4, 64 name: "Sass", 65 bgColor: "hotpink", 66 description: ` 67 text 68 ` 69 }, 70 { 71 id: 5, 72 name: "Firebase", 73 bgColor: "orange", 74 description: ` 75 text 76 ` 77 }, 78 { 79 id: 6, 80 name: "Git・Github", 81 bgColor: "black", 82 description: ` 83 text 84 ` 85 }, 86 { 87 id: 7, 88 name: "webpack", 89 bgColor: "skyblue", 90 description: ` 91 text 92 ` 93 } 94 ], 95 show: { 96 1: false, 97 2: false, 98 3: false, 99 4: false, 100 5: false, 101 6: false, 102 7: false 103 } 104 }; 105 }, 106 methods: { 107 toggleAccordion(skillId) { 108 this.show[skillId] = !this.show[skillId]; 109 } 110 } 111}; 112</script> 113 114<!-- Add "scoped" attribute to limit CSS to this component only --> 115<style lang="scss" scoped> 116.accordion { 117 width: 400px; 118 .label { 119 height: 30px; 120 margin-bottom: 10px; 121 text-align: center; 122 color: white; 123 cursor: pointer; 124 h2 { 125 font-weight: bold; 126 line-height: 30px; 127 } 128 .label-icon { 129 margin-left: 10px; 130 } 131 } 132 .box { 133 overflow: hidden; 134 background-color: white; 135 width: 100%; 136 display: flex; 137 img { 138 display: block; 139 width: 100px; 140 height: 100px; 141 } 142 } 143} 144.rotate { 145 transform: rotate(180deg); 146 transition: 0.3s; 147} 148.slide-enter-active { 149 transition-duration: 0.3s; 150 transition-timing-function: ease-in; 151} 152.slide-leave-active { 153 transition-duration: 0.3s; 154 transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 155} 156.slide-enter-to, 157.slide-leave { 158 max-height: 100px; 159 overflow: hidden; 160} 161.slide-enter, 162.slide-leave-to { 163 max-height: 0; 164 overflow: hidden; 165} 166</style> 167

②開閉状態を既存のskills配列の中で管理するパターン

js

1<template> 2 <div> 3 <div 4 v-for="skill in skills" 5 class="accordion" 6 v-bind:key="skill.id" 7 v-bind:style="{ border: '8px' + ' ' + 'solid' + ' ' + skill.bgColor, backgroundColor: skill.bgColor }" 8 > 9 <div 10 class="label" 11 v-on:click="toggleAccordion(skill)" 12 v-bind:style="{ backgroundColor: skill.bgColor }" 13 > 14 <h2> 15 {{ skill.name }} 16 <i class="fa fa-angle-down label-icon" v-bind:class="{ rotate : skill.show }"></i> 17 </h2> 18 </div> 19 <transition name="slide"> 20 <div class="box" v-show="skill.show"> 21 <!-- <img v-bind:src="require(`../assets/images/${skill.name}.png`)"> --> 22 <p>{{ skill.description }}</p> 23 </div> 24 </transition> 25 </div> 26 </div> 27</template> 28 29<script> 30export default { 31 name: "HelloWorld", 32 data() { 33 return { 34 skills: [ 35 { 36 id: 1, 37 show: false, // skillごとに開閉状態を保持 38 name: "HTML・CSS", 39 bgColor: "red", 40 description: ` 41 text 42 ` 43 }, 44 { 45 id: 2, 46 show: false, 47 name: "JavaScript", 48 bgColor: "gold", 49 description: ` 50 text 51 ` 52 }, 53 { 54 id: 3, 55 show: false, 56 name: "Vue.js", 57 bgColor: "forestgreen", 58 description: ` 59 text 60 ` 61 }, 62 { 63 id: 4, 64 show: false, 65 name: "Sass", 66 bgColor: "hotpink", 67 description: ` 68 text 69 ` 70 }, 71 { 72 id: 5, 73 show: false, 74 name: "Firebase", 75 bgColor: "orange", 76 description: ` 77 text 78 ` 79 }, 80 { 81 id: 6, 82 show: false, 83 name: "Git・Github", 84 bgColor: "black", 85 description: ` 86 text 87 ` 88 }, 89 { 90 id: 7, 91 show: false, 92 name: "webpack", 93 bgColor: "skyblue", 94 description: ` 95 text 96 ` 97 } 98 ] 99 }; 100 }, 101 methods: { 102 toggleAccordion: function(skill) { 103 this.skills.find(s => s.id === skill.id).show 104 = !this.skills.find(s => s.id === skill.id).show 105 } 106 } 107}; 108</script> 109 110<!-- Add "scoped" attribute to limit CSS to this component only --> 111<style lang="scss" scoped> 112.accordion { 113 width: 400px; 114 .label { 115 height: 30px; 116 margin-bottom: 10px; 117 text-align: center; 118 color: white; 119 cursor: pointer; 120 h2 { 121 font-weight: bold; 122 line-height: 30px; 123 } 124 .label-icon { 125 margin-left: 10px; 126 } 127 } 128 .box { 129 overflow: hidden; 130 background-color: white; 131 width: 100%; 132 display: flex; 133 img { 134 display: block; 135 width: 100px; 136 height: 100px; 137 } 138 } 139} 140.rotate { 141 transform: rotate(180deg); 142 transition: 0.3s; 143} 144.slide-enter-active { 145 transition-duration: 0.3s; 146 transition-timing-function: ease-in; 147} 148.slide-leave-active { 149 transition-duration: 0.3s; 150 transition-timing-function: cubic-bezier(0, 1, 0.5, 1); 151} 152.slide-enter-to, 153.slide-leave { 154 max-height: 100px; 155 overflow: hidden; 156} 157.slide-enter, 158.slide-leave-to { 159 max-height: 0; 160 overflow: hidden; 161} 162</style> 163

投稿2019/07/03 20:58

編集2019/07/04 04:11
KuwabataK

総合スコア306

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

take-t.t.

2019/07/04 03:50 編集

ご回答、そしてcodesandboxにサンプルコードまで頂きましてありがとうございます。 私の勉強不足で大変恐縮なのですが、まだ関数を直感的に理解できないためもう少し質問させていただいてもよろしいでしょうか? まずtoggleAccordionの引数「skill」はv-forで参照したskills内のオブジェクト一つ一つ、findの引数「s」はdata内の配列skillsのもつオブジェクト全てで、 返り値がtrueの場合にそのidが合致したskills内のオブジェクトを渡している、という事でしょうか?
KuwabataK

2019/07/04 03:55

「s」は配列skills内の一個一個のオブジェクト(skillオブジェクト)ですね。find関数の場合は、配列の先頭から要素をひとつずつ取り出して、第一引数に指定した関数の引数に渡し、関数の返り値がtrueになった一番はじめの要素を返します。 以下のようなコードを実行してみると挙動がわかりやすいかもしれません。 以下のURLのものを少し改変しました https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/find var array1 = [5, 12, 8, 130, 44]; var found = array1.find((s) => { console.log('sは' + s) return s > 10; }); console.log('findの返り値は' + found);
KuwabataK

2019/07/04 04:00

ただ、こういうid付きのデータを扱う時は、配列ではなく、idをキーに持った連想配列やMap Objectとして扱うと、findメソッド使わずに、idをキーにして配列要素を取り出すことができるのでよりコードがシンプルになると思います。 もしくは、アコーディオンの開閉が排他制御で良いのであれば、LanHmaさんのサンプルコードがシンプルで良いと思います。
take-t.t.

2019/07/04 04:18 編集

ご返信ありがとうございます。 頂いたコードの値を色々と動かしてみて理解できました、 今回は個別に開閉を行いたかったので、こちらのコードを使わせていただこうと思います。 他にもコードをよりシンプルにするために自分で色々といじってみようと思います。 そして改めまして、ご回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問