Vue Expand/Collapse Animation for FAQ Section
The article shows how to create a smooth FAQ expand‑collapse effect in Vue by storing each item’s visibility in a show flag, wrapping the answer in a transition component, and using JavaScript hooks to set explicit heights and overflow while rotating the arrow icon for visual feedback.
In many mobile apps a FAQ (question‑answer) module is required. The static page is simple, but the expand/collapse effect for the answer part can be tricky because the answer container’s height is determined by its inner content (height: auto), which makes CSS height transitions ineffective.
The solution is demonstrated with Vue. First, the data structure is defined (using TypeScript syntax for clarity):
interface QaItem {
Q: string; // question
A: string; // answer
show: boolean; // whether the answer is displayed
}
type QaList = QaItem[];The corresponding Vue template (simplified) looks like this:
<div class="qa panel">
<div class="qa__title">Common Questions</div>
<div class="list-qa">
<div v-for="(item, ind) in qaList" :key="ind" class="list-qa__item">
<div class="list-qa__question">
<span>{{ item.Q }}</span>
<span class="list-qa__question__arrow" />
</div>
<span class="list-qa__answer" v-show="item.show">{{ item.A }}</span>
</div>
</div>
</div>The answer visibility is controlled by the show property and rendered with v-show. For the expand/collapse animation, Vue’s transition component is used together with JavaScript hook functions that explicitly set the element’s height.
<transition
name="slide"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
>
<span v-show="item.show" class="list-qa__answer">{{ item.A }}</span>
</transition>JavaScript hook implementations:
beforeEnter(el) {
el.style.transition = '0.3s height ease-in-out';
el.style.overflow = 'hidden';
},
enter(el) {
el.style.height = 'auto';
const endHeight = window.getComputedStyle(el).height;
el.style.height = '0px';
el.offsetHeight; // force reflow
el.style.height = endHeight;
},
afterEnter(el) {
el.style.transition = '';
el.style.overflow = 'visible';
},
beforeLeave(el) {
el.style.transition = '0.3s height ease-in-out';
el.style.overflow = 'hidden';
},
leave(el) {
el.style.height = window.getComputedStyle(el).height;
el.offsetHeight; // force reflow
el.style.height = '0px';
},
afterLeave(el) {
el.style.transition = '';
el.style.overflow = 'visible';
}The arrow rotation effect is achieved with a simple CSS transition. When the arrow is clicked, a class is toggled to rotate the icon 180 degrees:
<span class="list-qa__question__arrow"
:class="{'list-qa__question__rotate-arrow': !item.show}"
@click="onClickProblem(ind)" /> onClickProblem(index) {
const qaItem = this.qaList[index];
this.$set(qaItem, 'show', !qaItem.show);
}Corresponding CSS:
.list-qa__question__arrow {
width: 12px;
height: 12px;
background: url(https://m.hellobike.com/resource/helloyun/21588/RIBiB_SketchPngf4c3c2445f4522fe182c1d02d45a6201fa03ecfb14550a3269204012abdcfa09) center no-repeat;
transition: transform .4s;
}
.list-qa__question__rotate-arrow {
transform: rotateZ(180deg);
transition: transform .4s;
}By explicitly setting the element’s height from 0 to the computed pixel value (and vice‑versa) and using overflow: hidden, the desired smooth expand/collapse animation is achieved, while the arrow rotation provides a visual cue for the user.
This tutorial demonstrates the principle of CSS transitions, the limitation of transitioning to/from auto, and a practical Vue‑based workaround.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
HelloTech
Official Hello technology account, sharing tech insights and developments.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
