Enhance the collapsible feature in Vue.js by integrating Bootstrap and anim

In the process of creating a side bar menu with collapse show/hide functionality, I am encountering some issues. The current CSS implementation is making the collapse action appear abrupt and unnatural.

I am looking to achieve a smooth sliding transition when an item is being closed. However, the problem arises when one item is already open and another item is clicked to open. It seems like the toggle effect is too forced and does not provide a smooth collapsing effect for the list.

Any suggestions on a better approach to achieve this desired smooth transition?

View Fiddle Implementation.

I am uncertain whether my current approach is correct or if there is something missing in my code?

new Vue({
  el: '#app',
  methods: {
    setActiveItemId(itemIndex) {
      if (itemIndex === this.activeItemId) {
        this.activeItemId = ''
        return
      }
      this.activeItemId = itemIndex
    }
  },
  data() {
    return {
      message: 'Hello Vue.js!',
      activeItemId: '',
      sideBar: [{
          name: "Dashboard",
          url: "/dashboard",
          icon: "ti-world",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Components",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        },
        {
          name: "Validation",
          url: "/components",
          icon: "ti-pencil-alt",
          children: [{
              name: "Buttons",
              url: "/components/buttons",
              icon: "fa-book",
            },
            {
              name: "Social Buttons",
              url: "/components/social-buttons",
              icon: "icon-puzzle",
            }
          ]
        }
      ]
    }
  },
  computed: {
    isActive() {
      return this.activeItemId !== ''
    }
  }
})
.collapse.show {
  display: block;
}

.collapse {
  display: none;
}

.list-unstyled {
  padding-left: 0;
  list-style: none;
}

.collapse.list-unstyled {
  padding-left: 15px;
}

nav.side-navbar {
  background: #fff;
  min-width: 250px;
  max-width: 250px;
  color: #000;
  -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1);
  z-index: 999;
}

nav.side-navbar ul a:hover {
  background: orange;
  color: #fff !important;
}

nav.side-navbar ul a {
  padding: 10px 15px;
  text-decoration: none;
  display: block;
  font-weight: 300;
  border-left: 4px solid transparent;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet"/>

<script src="https://unpkg.com/vue"></script>

<div id="app">
  <nav class="side-navbar">
    <ul class="list-unstyled">
      <li>
        <a>
          <i class="ti-home"></i>Home</a>
      </li>
      <li v-for="(x, itemIndex) in sideBar" :key="itemIndex">
        <a @click="setActiveItemId(itemIndex)">
          <i class="fa" :class="x.icon"></i>{{x.name}}
        </a>
        <ul :id="x.id" class="collapse list-unstyled" :class="{'show':activeItemId === itemIndex  && isActive}">
          <li v-for="y in x.children" :key="y.id">
            <a>{{y.name}}</a>
          </li>
        </ul>
      </li>
    </ul>
  </nav>
</div>

Answer №1

To implement transitions in Vue, you can utilize Vue's List Transitions feature (using the <transition-group> tag).

Modify the nested list ul as follows:

<ul :id="x.id" class="collapse list-unstyled show">
  <transition-group name="list">
    <li v-for="y in (activeItemId === itemIndex  && isActive ? x.children : [])" :key="y.name">
      <a>{{y.name}}</a>
    </li>
  </transition-group>
</ul>

Essentially, instead of simply hiding the <ul>, we are dynamically switching the v-for array from empty to filled based on conditions. Note that I have also corrected the keys for y.name since an incorrect prop was being used.

Additionally, apply the following CSS code for smoother transitions:

.list-enter {
  opacity: 0;
}
.list-enter-active {
  animation: slide-in .5s ease-out forwards;
}
.list-leave-to, .list-leave-active {
  opacity: 0;
  animation: slide-out .5s ease-out forwards;
}
@keyframes slide-in {
  from { height: 0; } to { height: 40px; }
}
@keyframes slide-out {
  from { height: 40px; } to { height: 0; }
}

For a demo and updated reference, check out this JSFiddle link. See the demo below.

<!-- JavaScript code here -->
<!-- CSS code here -->
<!-- HTML code here -->

Answer №2

If you're looking for a simpler workaround, consider including the following CSS snippet in your stylesheet.

.display {
  animation: width 0.5s, height 0.5s, transform 0.5s;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Passing parameters to a Vue.js @click method in Laravel 6: A step-by-step guide

How can I pass a parameter to a Vue component? Any help is appreciated! Blade Template @extends("./layouts.app") @section("title") {{$shop->shop_name}} @endsection @section("content") <addToCart></addToCart> @endsection Vue Js Code & ...

Creating a Jquery method to handle multi-line ellipsis for long text

Check out this fiddle I made: http://jsfiddle.net/mupersan82/JYuSY/ This snippet handles multiline ellipsis: $(function() { var ellipsisText = $('.multilineEllipseText'); var textContainer = $('.incidentCellBottomRight').height(); whi ...

What are some effective strategies for arranging multiple collapsible Bootstrap panels in an organized and visually appealing

I'm looking to use "collapsible bootstrap panels" to display a list, but I've run into an issue where certain panels don't align correctly when opened. For example, when I open the first panel in my code, all other panels shift down and sta ...

Guide on utilizing the h function in Vue3 for seamless binding and passing of properties and events from parent to child components

Utilizing Vue3 and naive ui for front-end development has been a challenge for me as I primarily focus on back-end development and lack expertise in front-end technologies. To enhance user interaction, I incorporated naive ui’s BasicTable along with an ...

Change the Vue3 PrimeVue theme or CSS file with a simple click or upon page load

One way to incorporate themes is by importing them in the main.js file at the root of the app: import 'primevue/resources/themes/arya-orange/theme.css'; However, I am exploring ways to dynamically switch CSS files based on the user's system ...

What could be causing the CSS transition to fail when the menu button is clicked?

After clicking on the menu, a class 'active' is added to both the 'div' and 'section' elements. https://i.stack.imgur.com/jbamR.png The following javascript code accomplishes the above functionality: <script> const ...

What is the preferable method: passing in $event or implying it?

Which approach is preferable: passing in the event when using it in a method or relying on implicit handling? Method 1: <input @keyup.enter="chooseMe($event)"/> Method 2: <input @keyup.enter="chooseMe"/> chooseMe(evt) { ...

Passing the v-model property from a child component to a parent in VueJS

Consider a scenario where there is a Vue component structured like the following: Vue.component('child-comp',{ props:['name', 'val'], data: function(){ return { picked: '' } }, ...

Utilizing PrimeVue Toast to showcase rich HTML content

Looking for a way to showcase a link in a primevue toast message without using the v-html directive or triple brackets. Anyone have any alternative solutions to suggest? ...

What is the recommended data type for the component prop of a Vuelidate field?

I'm currently working on a view that requires validation for certain fields. My main challenge is figuring out how to pass a prop to an InputValidationWrapper Component using something like v$.[keyField], but I'm unsure about the type to set for ...

Troubleshooting: Issues with jQuery animate() not functioning properly when adjusting CSS

Currently, I am experimenting with jQuery's .animate() function to change the background of a div when it is scrolled more than 100 pixels down a page. Previously, I had successfully used .css() for this purpose. JS: $(window).scroll(function () { ...

Enhanced Bootstrap Carousel with White Background Transitions

Having trouble articulating my issue, so here's a video that should clarify: https://example.com/video I'm using a bootstrap carousel template for a full-width slider. Despite my efforts to remove the white-space transitions, they persist. Am I ...

Encountering issues while trying to integrate Buefy with Nuxt

I recently set up a Nuxt project and decided to integrate Buefy following the instructions provided in the documentation. I completed the steps which included: Running npm i nuxt-buefy Adding 'nuxt-buefy' to the modules section in my nuxt.config ...

What is the best way to serve a .ttf file in node.js with the http and fs libraries?

I am running a basic node server and I have a CSS file that is trying to load a font from the server: @font-face{ font-family: 'NiagaraSolid-Reg'; src: url('http://localhost:1111/NIAGSOL.TTF'); } This is my attempt at servin ...

Modifying object properties in JavaScript

Looking to update a boolean attribute for an object in JavaScript (in this case, the object is part of fullPage.js library). I am interested in turning off the navigation attribute by setting it to false, and ideally would like to do this from a separate f ...

What is the best way to retrieve an array of objects that have a property matching another array?

In my array, I have data structured like this: array = [ { name: "john", tag: ["tag1", "tag2"] }, { name: "doe", tag: ["tag2"] }, { name: "jane", tag: ["tag2", "tag3"] } ]; My goal is to create a new array of objects that only contain elements with ...

Displaying content on the <div> element

Looking for recommendations for a jQuery plugin or JavaScript solution that allows me to load a full "view" into a <div> when a user clicks on a link. The challenge I'm facing is that I have 8 pages, with the Homepage consisting of 3 divisions: ...

Is there a way to confirm if I am taking a direct route instead of a detour?

My task seems simple enough: working with Vue3 and the Vue Router, I have a Home.vue component set at the route /, as well as a ProductDetails.vue component at the route /product. I am looking to trigger a specific action when a user navigates to my / rou ...

Enhanced auto-zoom feature with orientation change for iOS 7

My web page was functioning perfectly until the release of iOS 7 by Apple today, causing the layout to break when the screen orientation is changed. When focusing on a text area and rotating the screen to landscape view, the entire page zooms in. However, ...

Moving the sidebar from the app component to a separate component in Angular results in a situation where the sidebar does not maintain full height when the page is scrolled down

Within my Angular application, I have implemented a sidebar as a separate component. Previously, the content of the sidebar was housed within the main app component's HTML page, and it functioned properly by taking up the full height of the page when ...