Reputation: 6316
Is it possible using VueJS and vanilla JS to collapse one of many divs?
I have data within separate cards featuring a title and a body - I am wanting the body of the card to collapse/expand when the title is clicked.
VueJS is being used, and I would like to keep JQuery out of it for the time being, focusing on vanilla JS.
See this fiddle for an example of what it looks like. Please bare in mind that I am FULLY AWARE of not using the same ID for multiple elements - this was a quick demo for illustration purposes.
<div class="section">
<div class="title" @click="toggle"><span class="toggleIcon" id="toggleIcon">{{toggleIcon}}</span>Toggle This Section</div>
<hr/>
<div class="body" id="toggle">
<img style="height:100px" src="https://cdn.vox-cdn.com/thumbor/Pkmq1nm3skO0-j693JTMd7RL0Zk=/0x0:2012x1341/1200x800/filters:focal(0x0:2012x1341)/cdn.vox-cdn.com/uploads/chorus_image/image/47070706/google2.0.0.jpg">
</div>
</div>
The problem I have at the moment is that I can collapse a div, but not individual divs.
Upvotes: 4
Views: 19512
Reputation: 32694
Remember Vue.js is data driven, so changes in the DOM should reflect underlying data changes. In your case your case you should wrap the cards up in a component, so they track their own individual state, and then use a flag with v-show
to show and hide the section:
Vue Instance
Vue.component('card', {
template: '#card',
methods: {
toggle() {
this.showSection = !this.showSection
}
},
data() {
return {
showSection: true, // Flag to show section
imageUrl: 'https://cdn.vox-cdn.com/thumbor/Pkmq1nm3skO0-j693JTMd7RL0Zk=/0x0:2012x1341/1200x800/filters:focal(0x0:2012x1341)/cdn.vox-cdn.com/uploads/chorus_image/image/47070706/google2.0.0.jpg',
toggleIcon: '+'
}
}
})
Markup
<div class="section">
<div class="title" v-on:click="toggle">
<span class="toggleIcon" id="toggleIcon">{{toggleIcon}}</span> Toggle This Section
</div>
<hr/>
<!-- BIND v-show to the showSection data property to reflect changes -->
<div class="body" v-show="showSection">
<img v-bind:src="imageUrl">
</div>
</div>
Note that I'm not touching the DOM, I'm just toggling the showSection
flag and Vue is updating the DOM for me.
And here's the JSFiddle: https://jsfiddle.net/craig_h_411/j1wh75v9/1/
If you want to pass different content to each card you can use a slot:
<template id="card">
<div class="section">
<div class="title" v-on:click="toggle">
<span class="toggleIcon" id="toggleIcon">{{toggleIcon}}</span> Toggle This Section
</div>
<hr/>
<div class="body" v-show="showSection">
<!-- Add slot details here -->
<slot></slot>
</div>
</div>
</template>
Slots allow you to insert content into your component from the parent, as I'm only using a single slot, anything that I place between my component tags gets injected where I placed my slot tags in my component, so:
<card>I'm a card</card>
<card> Im another card</card>
In these examples, the message between the card tags will be added to the slot. Slots get compiled in the parent scope, not the scope of the component, so if you want to use data properties then they must be available in the parent, similarly you cannot access data properties and methods in the component from the slot data.
Here's the JSFiddle for that: https://jsfiddle.net/craig_h_411/ew1epg61/
Upvotes: 6
Reputation: 21
This another take on the collapsable card (with slots) similar to @craig_h:
<template>
<div class="section">
<div :class="[isActive ? 'active' : '', 'collapsible']" v-on:click="toggle">
Toggle This Section <span class="toggleIcon" id="toggleIcon">{{ toggleIcon }}</span>
</div>
<div :class="[isActive ? 'block' : 'none', 'content']" v-show="isActive">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
name: 'CollapsableCard',
components: {},
data() {
return {
isActive: false,
toggleIcon: '+',
}
},
methods: {
toggle() {
this.isActive = !this.isActive
},
},
}
</script>
<style scoped>
.collapsible {
background-color: #d8f8ea;
color: black;
cursor: pointer;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
}
.content {
padding: 0 18px;
overflow: hidden;
background-color: #eefcf6;
}
</style>
Upvotes: 0