How to Implement Transition Effect for Height Changes on v-if in Vuejs

I have a code snippet that effectively animates a v-if element by reducing its height to 0px. The animation is working well, but the issue arises when I need to specify the initial height of the element in CSS. While this is fine for a single element, I want to apply this animation to multiple elements. How can I resolve this so that the animation adjusts to any height dynamically?

<transition name="fadeHeight" mode="out-in">
<div v-if="something">
<p>content with variable height here</p>
</div>
</transition>

.fadeHeight-enter-active,
.fadeHeight-leave-active {
  transition: all 0.2s;
  height: auto;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
  opacity: 0;
  height: 0px;
}

Answer №1

It seems like there may be some missing code, but I have an idea of what you're trying to achieve.

Have you considered changing the transition to the max-height property instead?

.fadeHeight-enter-active,
.fadeHeight-leave-active {
  transition: all 0.2s;
  max-height: 230px;
}
.fadeHeight-enter,
.fadeHeight-leave-to
{
  opacity: 0;
  max-height: 0px;
}

If you ensure that the max height is greater than the tallest element, this approach should work for your needs. It's also worth noting that using overflow:hidden might be helpful. Keep in mind that if your elements vary significantly in height, the animation duration and delay could look off.

Check out this JSFiddle link for a visual example!

Answer №2

I encountered some challenges with this particular issue, and found that many of the solutions available were overly complicated in my opinion. After experimenting for a bit, I managed to come up with a simpler approach to achieve a smooth height transition for content set to "height: auto":

<template>
 <transition name="expand">
   <div v-show="isExpanded" ref="content">
     <slot />
   </div>
 </transition>
</template>

<script setup lang="ts">
import { onMounted, ref } from '@vue'

defineProps<{isExpanded: boolean}>()
const content = ref()
let height = ref()

onMounted(() => {
  height.value = `${content.value.getBoundingClientRect().height}px`
})
</script>

<style scoped lang="less">
.expand-leave-active,
.expand-enter-active {
  transition: all 350ms ease;
  overflow: hidden;
}

.expand-enter-to,
.expand-leave-from {
  height: v-bind(height);
}

.expand-enter-from,
.expand-leave-to {
  opacity: 0;
  height: 0;
}
</style>

I hope this solution proves helpful to someone else facing a similar challenge!

Answer №3

@ryantdecker seems to have the most popular solution, but personally, I like to keep my code concise by utilizing class binding instead:

<template>
 <!-- isShowing either data or computed... -->
 <div class="foo" :class="{ showing: isShowing, hidden: !isShowing }">
  <p>
   content here with variable height
  </p>
 </div>
</template>
...
<style>
.foo {
 height: auto;
 transition: max-height 0.5s;
 &.showing {
  max-height: 200px; /* MUST BE GREATER THAN height:auto */
 }
 &.hidden {
  max-height: 0px;
 }
}
</style>

If you want more customization options for further control, consider these:

  1. Include
    :style="{'max-height': computedHeight}"
  2. Implement different easing functions such as ease-in and ease-out in separate transitions within the .showing and .hidden classes.
  3. Use a cubic bezier transition speed for handling extremely long collapsing/expanding elements

The first modified option is useful when dealing with distinct items where heights are known, like images or flex rows that can be inspected using devtools. Example:

computed: {
 /**
  * @return {string} maximum height of the container in pixels if visible else zero
  */
 calcedHeight()
 {
   const elHeight = 80;
   const maxHeight = this.isShowing ? elHeight * this.elementCount : 0
   const maxHeightPx = maxHeight + 'px'
   return {
    'max-height': maxHeightPx
   }
 }
}

At this stage, it's easy to convert this into a component with props like isShowing, elHeight, and elCount.

Cubic Bezier

This section focuses on using cubic bezier transitions which can be effective for handling very tall elements (e.g., 5000px max-heights):

&.showing {                                                                                          
   transition: all 0.6s cubic-bezier(1, 0.01, 1, 0.01);                                                 
}                                                                                                       
&.hidden {                                                                                           
   transition: all 0.6s cubic-bezier(0.01, 1, 0.01, 1);                                                 
}

Answer №4

To address this issue, one common solution is utilizing a maxheight transition. However, there are situations where maxheight transitions may not be suitable. In such cases, a wrapper container component can be used to implement the transition as needed.

<template>
  <div
    class="fluid-wrapper"
    :class="{ 'in-transition': transitionState }"
    :style="computedDimensions"
    @transitionend="transitionState = 0"
  >
    <slot />
  </div>
</template>
<script>
export default {
  name: 'FluidContainer',
  props: ['trigger'],
  data() {
    return {
      oldRect: {
        height: null,
        width: null,
      },
      newRect: {
        height: null,
        width: null,
      },
      transitionState: 0,
      // 0: no Dimensions, no transition
      // 1: oldRect Dimensions, transition is on
      // 2: newRect Dimensions, transition is on
    };
  },
  computed: {
    computedDimensions() {
      if (!this.transitionState) {
        return null;
      }
      return this.transitionState === 1 ? this.oldRect : this.newRect;
    },
    dimensionsHasChanged() {
      return (
        this.newRect.height !== this.oldRect.height
        || this.newRect.width !== this.oldRect.width
      );
    },
  },
  watch: {
    trigger() {
      const oldStyle = getComputedStyle(this.$el);
      this.oldRect.height = oldStyle.height;
      this.oldRect.width = oldStyle.width;
      this.$nextTick(() => {
        const newStyle = getComputedStyle(this.$el);
        this.newRect.height = newStyle.height;
        this.newRect.width = newStyle.width;
        if (this.dimensionsHasChanged) {
          this.transitionState = 1;
          window.requestAnimationFrame(() => {
            this.transitionState = 2;
          });
        } else {
          this.transitionState = 0;
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.fluid-wrapper {
  /* overflow: hidden; */
  height: fit-content;
  width: fit-content;
  &.in-transition {
    transition: all 0.3s;
  }
}
</style>

Usage:

<FluidContainer :trigger="some-variable">
    <!-- Any Reactive Content -->
</FluidContainer>

The ‘trigger’ prop must be provided for this functionality to work. It should be linked to a state variable that triggers changes in the inner content. The wrapper will monitor the trigger to detect dimension changes and initiate the transition accordingly.

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

Issues related to validation prior to submission

Having trouble with a VeeValidate example from the documentation. The example can be found here. I seem to be missing something crucial but can't figure out what it is. For some reason, my form always validates as valid, even when no text is entered ...

Align a button within an input field to be in line with a stacked label in

I'm currently facing some issues with Ionic because it's not placing my button where I want it to be: https://i.stack.imgur.com/qtUQJ.png My goal is to have the button on the same line as the input, similar to the clear-button. This is the cod ...

The color of the left arrow on the tooltip cannot be altered

Currently, I am using a tooltip that is positioned on the right side: <i class="fas fa-question-circle text-blue" data-toggle="tooltip" data-placement="right" title="hello" ></i> I have attempted to modify the color of the too ...

Traversing JSON Data using Vanilla JavaScript to dynamically fill a specified amount of articles in an HTML page

Here is the code along with my explanation and questions: I'm utilizing myjson.com to create 12 'results'. These results represent 12 clients, each with different sets of data. For instance, Client 1: First Name - James, Address - 1234 Ma ...

Create an eye-catching hexagon shape in CSS/SCSS with rounded corners, a transparent backdrop, and a

I've been working on recreating a design using HTML, CSS/SCSS in Angular. The design can be viewed here: NFT Landing Page Design Here is a snippet of the code I have implemented so far (Typescript, SCSS, HTML): [Code here] [CSS styles here] [H ...

Having trouble with HTML - JavaScript function not processing responseText?

On my website, there is a button array that displays the position of a robot by reading a text file using a php/ajax combo. The script initially sets all buttons to the same color and changes the color of the button to represent the robot's position. ...

Issue with Vue.js - Double curly braces not rendering data on the page

I've recently delved into the world of vue.js Strangely enough, the double curly braces syntax doesn't seem to be rendering for me. However, when I utilize the v-text directive, everything falls into place. Here's a snippet of my code: HTM ...

Python - Selenium: A guide to locating elements based on color

I have a situation where I need to select only the "Claim" button from the first div, even though both div cases are very similar. Is using background color a good way to identify my element? Or do you have any other ideas? 1. <div class="well well-sm ...

How to activate PurgeCSS for external CSS sources

I have integrated the @mdi/font into my project built with Nuxt for server-side rendering (SSR). Is there a way to enable purgeCSS specifically for the CSS coming from @mdi/font? nuxt.config.js module.exports = { css: [ '@/assets/scss/a ...

Is there a way to change the background color of product blocks on Shopify in a dynamic

Is there a way to customize the background colors of product blocks on Shopify by modifying the shop code? Take a look at this example: Mockup showing alternating block backgrounds Currently, all product blocks in the grid have a black background with wh ...

How can I switch between columns in a dual-column layout?

In my latest project, I utilized CSS Flexbox to create a sophisticated two-column layout that allows toggling each column independently. While this functionality is exactly what I need, I can't help but feel that my current method is somewhat cumberso ...

Order comments based on their category using vue.js

I have a component form with select filter: <template> <div class="form-group"> <select name="" v-model="filterRev" @change="filterReviews(filterRev)" class="form-control" id=""> <option ...

The Bootstrap-vue table stops displaying details when the data is refreshed

I am utilizing a bootstrap-vue table that is connected to a computed property pulling data from my vuex store. Each row contains a show_details button that expands a second row, following the guidelines here: The issue arises when there are changes in th ...

Changing position with flair in CSS

Is there a way to create a transition effect on an element when it changes position from static to fixed? I attempted using the code: transition: position 2s linear;, however, it does not seem to have any impact. Are there any other methods I could experi ...

I am facing an issue where the images (IMG) do not align properly when I test the responsiveness using Chrome

Seeking assistance with a design challenge I encountered. The first image showcases a well-structured table on desktop view, while the second image displays an undesired layout when examined using Chrome DevTools. A link to the problematic layout can be fo ...

My HTML table is not printing at full width

Seeking assistance with creating a printable calendar using an HTML table. The calendar prints perfectly on a Mac, but when attempted on Windows in all browsers, it adds a 3" margin at the top regardless of CSS print settings. The client requires the cal ...

How to disable the underline styling for autocomplete in a React Material UI component

Seeking assistance with removing underline styling and changing text color upon focus within the autocomplete component of React Material UI. Struggling to locate the specific style needing modification. Appreciate any help in advance! ...

Unable to locate the element within the specified div using xpath or css selector

I have attempted to retrieve the output "January 27" using this code: task_offer_deadline = driver.find_element_by_xpath('//*[@id="table"]/div[2]/div/a/div[1]/span[2]/div/em').text The xpath was obtained from Google Chrome Console. Here is a sn ...

Extract the property value and save it as an array in Vue

Looking to extract all values of a specific property and save them as an array. I attempted the following method: data() { return { roomList: null } }, methods: { getRooms() { var that = this axios.get('http://local ...

What could be causing my cross fade to not repeat as intended?

I created a basic background image cross fader using the code found at http://jsfiddle.net/jRDkm/2/. This code was inspired by . However, I'm encountering an issue where the slideshow only repeats once before fading to white. How can I modify the cod ...