Creating an expand and collapse animation in a `flex` accordion can be achieved even when the container size is fixed using

Good day,

I am in need of a fixed-height HTML/CSS/JS accordion. The requirement is for the accordion container's height to be fixed (100% body height) and for any panel content overflow, the scrollbar should appear inside the panel's content div rather than the accordion container itself! I have created a pen:

https://codepen.io/lotrmj/pen/bGJEzrp
to showcase the general concept. Everything functions as expected, but there is no animation...

Is it feasible to add animation for the expand/collapse action similar to: https://vuetifyjs.com/en/components/expansion-panels/#variant ? Or is it not possible due to the variable height of panel headers and their corresponding contents? If it can be done, could someone provide me with some assistance?

I have accomplished this previously using the jQuery UI library, but I am looking for more contemporary solutions/libraries...

I attempted to find examples but came up empty-handed on similar issues.

<template>
  <div id="nav">
      <template v-for="i in 5">
        <div class="panel-header">
          <span>PANEL {{i}}</span>
          <button @click="expandOrCollapse(i)">{{i == activePanel ? "Collapse" : "Expand"}}</button>
        </div>
        <div class="panel-content" v-if="i == activePanel">
          <div class="content">
            Very long panel {{i}} content with scrollbar...
          </div>
        </div>
      </template>
  </div>
  <div id="main">MAIN DIV</div>
</template>

<script>
export default {
  data() {
    return {
      activePanel: 2
    };
  },
  methods: {
    expandOrCollapse(i) {
      if (i == this.activePanel) {
        this.activePanel = undefined;
      } else {
        this.activePanel = i;
      }
    }
  }
};
</script>

<style>
  html, body {
    height: 100%;
    margin: 0;
  }
  body {
    display: flex;
  }
  #app {
    flex-grow: 1;
    display: flex;
  }
  #nav {
   width: 400px;
   border-right: 1px solid #ccc;
   overflow: auto;
   display: flex;
   flex-direction: column;
  }
  #main {
   flex-grow: 1;
   display: flex;
   align-items: center;
   justify-content: center;
  }
  .panel-header, .panel-content {
    border-bottom: 1px solid #ddd;
    padding: 5px;
  }
  .panel-header span {
    padding-right: 3px;
  }
  .panel-content {
    overflow: auto;
    flex-grow: 1;
  }
  .content {
    height: 1000px;
  }
</style>

Answer №1

When you use v-if, the element is actually removed from the DOM, which means there's no opportunity to showcase any animation.

An alternative approach would be to utilize a CSS grid layout, where each panel header and content take up a grid row. Then, apply a CSS transition between 0fr and 1fr to hide or show the panel content accordingly. Additionally, ensure that the padding-top and padding-bottom are also removed for the content when it's hidden.

Vue.createApp({
  data() {
    return {
      activePanel: 2
    };
  },
  computed: {
    templateRows() {
      return Array(5)
        .fill()
        .flatMap((_, i) => ['max-content', (i + 1) === this.activePanel ? '1fr' : '0fr'])
        .join(' ');
    },
  },
  methods: {
    expandOrCollapse(i) {
      if (i == this.activePanel) {
        this.activePanel = undefined;
      } else {
        this.activePanel = i;
      }
    }
  }
}).mount('#app');
html, body {
  height: 100%;
  margin: 0;
}
body {
  display: flex;
}
#app {
  flex-grow: 1;
  display: flex;
}
#nav {
 width: 400px;
 border-right: 1px solid #ccc;
 overflow: auto;
 display: grid;
 transition: grid-template-rows 500ms;
}
#main {
 flex-grow: 1;
 display: flex;
 align-items: center;
 justify-content: center;
}
.panel-header, .panel-content {
  border-bottom: 1px solid #ddd;
  padding: 5px;
}
.panel-header span {
  padding-right: 3px;
}
.panel-content {
  overflow: auto;
  flex-grow: 1;
  transition: padding-top 500ms, padding-bottom 500ms;
}
.panel-content:not([aria-expanded="true"]) {
  padding-top: 0;
  padding-bottom: 0;
}
.content {
  height: 1000px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/3.4.21/vue.global.prod.min.js" integrity="sha512-tltvjJD1pUnXVAp0L9id/mcR+zc0xsIKmPMJksEclJ6uEyI8D6eZWpR0jSVWUTXOKcmeBMyND+LQH4ECf/5WKg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div id="app">
  <div id="nav" :style="{ gridTemplateRows: templateRows }">
      <template v-for="i in 5">
        <div class="panel-header">
          <span>PANEL {{i}}</span>
          <button @click="expandOrCollapse(i)">{{i == activePanel ? "Collapse" : "Expand"}}</button>
        </div>
        <div class="panel-content" :aria-expanded="i == activePanel">
          <div class="content">
            Very long panel {{i}} content with scrollbar...
          </div>
        </div>
      </template>
  </div>
  <div id="main">MAIN DIV</div>
</div>

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

Get a webpage that generates a POST parameter through JavaScript and save it onto your device

After extensive research, I have hit a roadblock and desperately need help. My task involves downloading an HTML page by filling out a form with various data and submitting it to save the responses. Using Firebug, I can see that my data is sent over POST, ...

Excellent JavaScript library for capturing screenshots of the entire screen

Is there a good JavaScript library that enables users to take a screenshot of a webpage and customize the size before saving it to their computer? I am specifically looking for a pure JS solution where users can simply click on a button and have a "save a ...

Tips for updating and triggering useEffect when the router changes in React JS

I have a ChartPage function that is being called three times within three different routers. Inside the ChartPage function, I am using "useEffect" to update some variables based on the selected route. However, for some reason, it only triggers once, and wh ...

Is there a way to pass a c struct pointer into javascript using ffi?

I need help passing a pointer to a struct to a method in nodejs using ffi. I am encountering an error where the type of the JavaScript struct I created cannot be determined. How can I resolve this issue? Previously, I was able to successfully implement si ...

Comparison between Bootstrap 4 and Firefox Preload Error

Hello fellow front end developers who are experiencing an issue with CSS preload in Firefox versions above 74.0. I have encountered a problem with the following code snippet: <link rel='preload' as='style' onload="this.onload=null; ...

What could be causing my Angular to malfunction?

I've encountered some challenges while trying to run angular on my computer. I have been studying angular through demos on w3 schools that showcase various components. Currently, I am experimenting with this one: http://www.w3schools.com/angular/try ...

Having trouble with importing and receiving the error message "Module 'clone' not found."

When trying to use clone.js in Angular 2, I imported it with import * as clone from 'clone'. It was listed in my package.json dependencies and successfully imported into node_modules. However, when viewing the output, I encountered the error mes ...

The test() function in JavaScript alters the output value

I created a simple form validation, and I encountered an issue where the test() method returns true when called initially and false upon subsequent calls without changing the input value. This pattern repeats with alternating true and false results. The H ...

Troubleshooting lack of output in Console.log (NodeJs)

I am facing an issue where I can't see any output when trying to console log a JavaScript code using Node.js. const pokemon = require('pokemon'); pokemon.all(); var name = pokemon.random(); console.log(name); I am unable to even display a ...

Various height divs are arranged in two columns and float accordingly

My goal is to have two columns that stack divs of varying heights in the order they appear. These divs are created dynamically, causing some challenges with alignment when floated at 50% width. For example, div #4 may end up being significantly taller tha ...

Adjust the sliders according to the current time

I am looking to display different sliders based on the time of day. For instance, 'slider set 1' from 0-9am, 'slider set 2' from 9am-12pm, and so forth. I am new to java script and need assistance in solving this challenge. Below is the ...

Generating fresh Mongodb Records versus Appending to a Document's Collection

A device that records temperature data every second feeds into a real-time chart using Meteor.js to display the average temperature over the past 5 seconds. Should each temperature reading be saved as a separate MongoDB document, or should new readings be ...

What distinguishes node from node js?

Following the instructions on https://nodejs.org/en/download/package-manager/, I installed Node.js. Upon checking the current version using: :~/Downloads$ nodejs -v I discovered that I was running v4.2.6, which is an older version. Deciding to update, I ...

The index "is_ajax" has not been defined

I attempted to create a simple login functionality using Ajax. Following a tutorial that didn't use classes and functions in PHP, I tried restructuring the code with classes and functions. However, I encountered the error: Undefined index: is_ajax He ...

Encountering an issue in a Next.js application while building it, where an error is triggered because the property 'protocol' of 'window.location' cannot be destructured due to being undefined

While building my nextjs application, I encountered the following error. My setup uses typescript for building purposes, although I am only using JavaScript. Build error occurred: TypeError: Cannot destructure property 'protocol' of 'window ...

Is there a way to modify the header color in LESS? I attempted to do so, but unfortunately, my changes were unsuccessful

I have encountered an issue with my HTML code. Here's a snippet of it: <html> <head> <link rel="less/less.js" href="css/style.less"> </head> <header> This is a header. </header> </html> Within style.less ...

Getting an UnhandledPromiseRejectionWarning while attempting to navigate through Google Maps using Node.js Puppeteer

(node:15348) UnhandledPromiseRejectionWarning: Error: Execution context was destroyed due to a potential navigation issue. const browser = await puppeteer.launch({headless: false}); const page = await browser.newPage(); page.goto("https://www.google. ...

Forward users to specific date and time on Everwebinar link

Is there a way to automatically redirect visitors of my custom Everwebinar confirmation page (on my domain) to a specific URL at a set date and time that is included in the confirmation page URL? Here is an example of what the confirmation page URL looks ...

Using jQuery to efficiently handle dynamically added elements by combining the ".on" and ".each" methods

I am currently in the process of dynamically inserting new elements into my page, and these elements have a specific structure that is represented as follows: <div id="cars"> <input type="text" name="car_added[]" class="car" /> <inp ...

Implementing a jQuery function to trigger window resize on window load

I'm facing an issue with my resize function where the contents within a window change when it reaches a certain width. I'm trying to achieve this same effect on window load but haven't been successful so far. Here's what I've attem ...