Creating a responsive accordion content with Vue and jQuery

Published Oct 13, 2017Last updated Feb 17, 2018
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:

HOME - CATERING - OFFICE.jpg

MENU > Dinner.jpg

Mobile:

Mobile - CATERING.jpg

Mobile - MENU.jpg

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:

  1. Foundation to handle the responsive side,
  2. jQuery to handle the accordion magic, and
  3. 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

  1. 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.

  1. 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 -->
  1. 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()
  1. 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"; // => "&#43;"
    }

    // Add a minus sign.
    .ui-accordion-header-active:after {
      content: " \2212"; // => "&#8722;"
    }
  }
}

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:

Discover and read more posts from LAU TIAM KOK
get started