Creating a responsive accordion content with Vue and jQuery
Accordions are the solution you would think of when you want to organise and navigate multiple documents in a single container. They can be used for switching between items in the container and that makes your layout and content tidier. There are frameworks help you to do that, such as Zurb Foundation, Bootstrap, and jQuery UI.
The challenge
However, they can be quite complicated to set up with many data
attributes you need write into your HTML tags. I find JQuery is the easiest:
<script>
$(function(){
$("#accordion").accordion()
})
</script>
<div id="accordion">
<h3>Section 1</h3>
<div>
<p>
Mauris mauris ante, blandit et, ultrices a, suscipit eget, quam. Integer
ut neque. Vivamus nisi metus, molestie vel, gravida in, condimentum sit
amet, nunc. Nam a nibh. Donec suscipit eros. Nam mi. Proin viverra leo ut
odio. Curabitur malesuada. Vestibulum a velit eu ante scelerisque vulputate.
</p>
</div>
<h3>Section 2</h3>
<div>
<p>
Sed non urna. Donec et ante. Phasellus eu ligula. Vestibulum sit amet
purus. Vivamus hendrerit, dolor at aliquet laoreet, mauris turpis porttitor
velit, faucibus interdum tellus libero ac justo. Vivamus non quam. In
suscipit faucibus urna.
</p>
</div>
<h3>Section 3</h3>
<div>
<p>
Nam enim risus, molestie et, porta ac, aliquam ac, risus. Quisque lobortis.
Phasellus pellentesque purus in massa. Aenean in pede. Phasellus ac libero
ac tellus pellentesque semper. Sed ac felis. Sed commodo, magna quis
lacinia ornare, quam ante aliquam nisi, eu iaculis leo purus venenatis dui.
</p>
<ul>
<li>List item one</li>
<li>List item two</li>
<li>List item three</li>
</ul>
</div>
<h3>Section 4</h3>
<div>
<p>
Cras dictum. Pellentesque habitant morbi tristique senectus et netus
et malesuada fames ac turpis egestas. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia Curae; Aenean lacinia
mauris vel est.
</p>
<p>
Suspendisse eu nisl. Nullam ut libero. Integer dignissim consequat lectus.
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos.
</p>
</div>
</div>
Another challenge using Foundation's that I had for a project was that the accordion is only required for mobile/ small screens, there is a completely different layout for desktop/ large screens.
Desktop:
Mobile:
A solution
The layouts above are quite complex, especially the number 1. It is quite impossible to toss a single set of HTML elements for both desktop and mobile, while the content for these layouts is constant. So, writing two sets of HTML elements - one for desktop and one for mobile probably makes more sense. But writing two sets of HTML elements with Foundation manually is expensive, I need a client-side template rendering engine to ease the writing. There are plenty of frameworks can help you out, such as React, Angular, or JsRender. Note that you also can use a server-side template rendering software too.
I decided to go for:
- Foundation to handle the responsive side,
- jQuery to handle the accordion magic, and
- Vue to handle the data and template rendering
The only help I need from Vue is list rendering which is really easy to use:
<ul id="example-1">
<li v-for="item in items">
{{ item.message }}
</li>
</ul>
var example1 = new Vue({
el: '#example-1',
data: {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
}
})
The biggest take-away from Vue is that it does not cost you a steep learning curve at all. The HTML and JavaScript code written in Vue is pleasant to read. Personally, I enjoy writing code with Vue than its counterparts - Angular and React.
The code
- Create the elements for Foundation to handle the responsive:
<!-- grid container -->
<div class="grid-container no-padding-for-small-only">
<!-- grid x -->
<div class="grid-x">
<!-- cell -->
<div class="cell medium-12" id="food-lunch">
<!-- fluid-columns -->
<div class="fluid-columns padding-top-60 padding-bottom-60 hide-for-small-only">
...
</div>
<!-- fluid-columns -->
<!-- accordion -->
<div class="accordion accordion-items show-for-small-only">
...
</div>
<!-- accordion -->
</div>
<!-- cell -->
</div>
<!-- grid x -->
</div>
<!-- grid-container -->
show-for-small-only
and hide-for-small-only
are all I need.
- Create the elements for Vue to render the data:
// Desktop
<!-- fluid-columns -->
<div class="fluid-columns padding-top-60 padding-bottom-60 hide-for-small-only">
<!-- vue - loop -->
<template v-for="(categoryItems, index) in items">
<h4 class="heading-food">{{ index }}</h4>
<div class="food-body">
<template v-for="item in categoryItems">
<h5 class="title-food">{{ item.title }}</h5>
<p class="description-food">{{ item.description }}</p>
</template>
<br/>
<br/>
</div>
</template>
<!-- vue - loop -->
</div>
<!-- fluid-columns -->
// Mobile
<!-- accordion -->
<div class="accordion accordion-items show-for-small-only">
<!-- vue - loop -->
<template v-for="(categoryItems, index) in items">
<h4 class="heading-accordion">{{ index }}</h4>
<div class="accordion-body">
<div class="accordion-text">
<template v-for="item in categoryItems">
<h5 class="title-food">{{ item.title }}</h5>
<p class="description-food">{{ item.description }}</p>
</template>
</div>
<br/>
<br/>
</div>
</template>
<!-- vue - loop -->
</div>
<!-- accordion -->
- Create a
.js
file for Vue, jQuery and Foundation to work together:
...
...
...
// Render template with Vue.
var element = document.getElementById('food-lunch')
if (element !== null) {
var getData = await axios.get('./food-lunch.json')
var cateringFood = new Vue({
el: '#food-lunch',
data: {
items: getData.data
}
})
}
// Create the accordion menu with jQuery.
$('.accordion').accordion({
heightStyle: "content",
collapsible: true
})
// Initiate foundation.
// Must do it after Vue has rendered the view.
$(document).foundation()
- Lastly, of course you need a
.css
file to style the template. The example code (LESS):
...
...
...
.fluid-columns {
-webkit-column-count: 2; /* Chrome, Safari, Opera */
-moz-column-count: 2; /* Firefox */
column-count: 2;
-webkit-column-gap: 40px; /* Chrome, Safari, Opera */
-moz-column-gap: 40px; /* Firefox */
column-gap: 40px;
&.padding-top-60 {
padding-top: 60px;
}
&.padding-bottom-60 {
padding-bottom: 60px;
}
.description-food {
font-size: @text-font-size-medium;
}
}
/* Small only */
@media screen and (max-width: 39.9375em) {
.fluid-columns {
-webkit-column-count: 1; /* Chrome, Safari, Opera */
-moz-column-count: 1; /* Firefox */
column-count: 1;
}
.accordion-items {
background-color: #ffffff;
.heading-accordion {
padding-left: 20px;
padding-right: 20px;
padding-top: 20px;
padding-bottom: 20px;
margin: 0;
background-color: #f7f7f7;
border-top: 1px solid #ebebeb;
border-bottom: 1px solid #ebebeb;
}
.accordion-text {
padding: 20px;
}
/** Overwrite jQuery UI */
// Add a plus sign.
.ui-accordion-icons:after {
content: " \002B"; // => "+"
}
// Add a minus sign.
.ui-accordion-header-active:after {
content: " \2212"; // => "−"
}
}
}
Suggestions
Let me know what you think and what would be your solutions. Anything suggestions, please leave a comment below. Hope this helps. You can download the working sample above on GitHub. You also can view them at:
<?php $OUTPUT_FOR_DUMMIES([‘accordeon’, $accordeon_data]);