Home > Blockchain >  How to dynamically fill tabs based on JSON data Vue.js/JavaScript
How to dynamically fill tabs based on JSON data Vue.js/JavaScript

Time:09-30

I'm currently working on a small project which requires me to create tabs which are dynamically filled with data from a local .json file. I've got the tab titles working, however each time a tab is clicked it only shows the content from the first data index. I've tried looping through the data and a few other things I've forgotten at this point with no luck so any help would be appreciated.

One thing to note is that this is my first time using Vue. I'm aware there's likely a much easier way to do this with Vue itself, however I'd rather use vanilla JS at this stage as I'm more familiar with it. I also haven't used tab id's as it's meant to be dynamic.

This is the code I have at the moment, let me know if there's anything you'd like me to add.

HTML:

<template>
<section >
    <div >
        <button  v-for="data in data" :key="data.title">{{ data.title }}</button>
    </div>
    <div  v-for="data in data" :key="data.content">
        <p v-html="data.content"></p>
    </div>
</section>

CSS:

<script>
import data from '../assets/data.json'

export default {
  props: ['title', 'content'],
  data() {
    return {
      data: data
    }
  }
}
</script>

<style>

.tab__container {
    display: none;
}

@media (min-width: 769px) {
    .tab__container {
        display: flex;
        border: 1px solid #d2d2d2;
    }

    .tab__sidebar {
        flex-direction: row;
        width: 125px;
        flex-shrink: 0;
        background: #cccccc;
    }

    .tab__button {
        display: block;
        padding: 10px;
        background: #eeeeee;
        border: none;
        width: 100%;
        outline: none;
        cursor: pointer;
    }

    .tab__button:active {
        background: #dddddd;
    }

    .tab__button:not(:last-of-type) {
        border-bottom: 1px solid #cccccc;
    }

    .tab__button.is__active {
        font-weight: bold;
        border-right: 2px solid #009879;
        background: #dddddd;
    }

    .tab__content {
        padding: 20px;
        font-size: 18px;
        display: none;
    }

    .tab__content.is__active {
        display: block;
    }
}
</style>

JavaScript:

document.querySelectorAll(".tab__button").forEach(button => {
    button.addEventListener("click", () => {
        var sidebar = button.parentElement;
        var tabContainer = sidebar.parentElement;
        var tabToActivate = tabContainer.querySelector(".tab__content");
      
        sidebar.querySelectorAll(".tab__button").forEach(button => {
            button.classList.remove("is__active");
        });

        tabContainer.querySelectorAll(".tab__content").forEach(tab => {
            tab.classList.remove("is__active");
        });

        button.classList.add("is__active");
        tabToActivate.classList.add("is__active");
    });
});

.json data:

[
    {
      "title": "Section 1",
      "content": "<p>Maecenas nec semper ante, pellentesque posuere lorem. Nullam ipsum massa, consequat eget urna ut, pulvinar dignissim lorem. Nulla facilisi. Nam mattis eleifend metus. Fusce at commodo lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus pellentesque elit sem, vel blandit posuere.</p>"
    },
    {
      "title": "Section 2",
      "content": "<p>Mauris a orci sodales, scelerisque velit vitae, gravida nisl. Ut non laoreet eros, vel laoreet nisi. Praesent sed dolor dui. Proin non fringilla quam. Aliquam erat volutpat. Vestibulum vel arcu semper, lobortis turpis ac, ultricies nisi. Praesent id.</p>"
    },
    {
      "title": "Section 3",
      "content": "<p>Sed elementum sapien ut sapien imperdiet, eu venenatis enim rhoncus. Praesent euismod tincidunt rhoncus. Duis cras amet:</p><ul><li>List item one</li><li>List item two</li><li>List item three</li></ul>"
    },
    {
      "title": "Section 4",
      "content": "<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>"
    }
  ]

CodePudding user response:

    const data = [
        {
          "title": "Section 1",
          "content": "<p>Maecenas nec semper ante, pellentesque posuere lorem. Nullam ipsum massa, consequat eget urna ut, pulvinar dignissim lorem. Nulla facilisi. Nam mattis eleifend metus. Fusce at commodo lorem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Phasellus pellentesque elit sem, vel blandit posuere.</p>"
        },
        {
          "title": "Section 2",
          "content": "<p>Mauris a orci sodales, scelerisque velit vitae, gravida nisl. Ut non laoreet eros, vel laoreet nisi. Praesent sed dolor dui. Proin non fringilla quam. Aliquam erat volutpat. Vestibulum vel arcu semper, lobortis turpis ac, ultricies nisi. Praesent id.</p>"
        },
        {
          "title": "Section 3",
          "content": "<p>Sed elementum sapien ut sapien imperdiet, eu venenatis enim rhoncus. Praesent euismod tincidunt rhoncus. Duis cras amet:</p><ul><li>List item one</li><li>List item two</li><li>List item three</li></ul>"
        },
        {
          "title": "Section 4",
          "content": "<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>"
        }
     ];
  
 new Vue({
  el: '#app',
  data() {
    return {
      data,
      activeTabIndex: 0,
    }
  },
  computed: {
    activeContent() {
      return this.data[this.activeTabIndex]?.content;
    }
  
  }
 })
        .tab__container {
    display: none;
}

@media (min-width: 769px) {
    .tab__container {
        display: flex;
        border: 1px solid #d2d2d2;
    }

    .tab__sidebar {
        flex-direction: row;
        width: 125px;
        flex-shrink: 0;
        background: #cccccc;
    }

    .tab__button {
        display: block;
        padding: 10px;
        background: #eeeeee;
        border: none;
        width: 100%;
        outline: none;
        cursor: pointer;
    }

    .tab__button:active {
        background: #dddddd;
    }

    .tab__button:not(:last-of-type) {
        border-bottom: 1px solid #cccccc;
    }

    .tab__button.is__active {
        font-weight: bold;
        border-right: 2px solid #009879;
        background: #dddddd;
    }

    .tab__content {
        padding: 20px;
        font-size: 18px;
        display: block;
    }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

    <main id="app">
      <section >
        <div >
            <button 
                v-for="(data, index) in data"
                :key="data.title"
                @click="activeTabIndex = index">{{ data.title }}</button>
        </div>
        <div >
            <p v-html="activeContent"></p>
        </div>
      </section>
    </main>

  • Related