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

Conceal the Angular Bootstrap modal instead of shutting it down

I am utilizing Angular Bootstrap Modal to launch multiple modals, including a video player. http://angular-ui.github.io/bootstrap/#/modal My goal is to maintain the video's current position even when the modal is closed. This way, when I reopen the ...

Hiding the C3 tooltip after engaging with it

I'm currently expanding my knowledge on utilizing C3.js for creating charts, and one aspect I'm focusing on is enhancing the tooltip functionality. Typically, C3 tooltips only appear when you hover over data points as demonstrated in this example ...

What is the process for incorporating a custom attribute into an element with Vue directives?

One of the challenges I'm facing is dealing with a custom attribute called my-custom-attribute. This attribute contains the ID for the element that needs to have the attribute added or removed based on a boolean value. Although I've implemented ...

Customizing styles based on conditions in a v-for loop using Vue.js

I have created a calendar using momentjs and I would like to customize the appearance of weekends with a different color. The dates and days are displayed using a v-for loop as follows: <tr v-for="(day, index) in days" :key="index" ...

Numerous textareas fail to function properly according to JQuery's standards

Need help with resizing multiple textarea elements dynamically as the user types in them (on the Y-axis). Currently, I have code that successfully resizes a single textarea, but it does not work when there are multiple textareas on the page. Here is the c ...

What is the method for altering the look of a button using JavaScript?

As a beginner in web development, I have a question. Typically, I know how to style the submit button when it's created in the HTML page using CSS. However, I'm wondering if it's possible to apply CSS styling to the JavaScript block instead. ...

Is there a CSS3 Selector With Similar Functionality to jQuery's .click()?

For a few years now, I have been utilizing a pure CSS navigation system. However, with the recent increase in mobile site projects at my workplace, I am encountering issues with drop-down menus not functioning properly on mobile devices. Despite this chall ...

Guide to setting up routes in nuxt.js

I am building a blog website with Nuxt.Js on the domain xyz.com. I would like to display different pages based on country code, such as xyz.com/en/home and xyz.com/en/about. Can someone guide me on how to define routes in Nuxt.js for displaying blog conte ...

Issue with interactive rows in a table ( Rails Version 4 )

I am encountering an issue with a table that is linked to a customer show page _table.html.erb <table class="table table-striped table-bordered table-hover table-condensed"> <thead> <tr> <th width="50"><%= sort_link ...

Checkbox will be automatically selected and the input box will be automatically populated when a value is detected

I have recently set up a sandbox for testing purposes: https://codesandbox.io/s/sweet-agnesi-qbf7g?file=/src/components/HelloWorld.vue Let's discuss a scenario where we have an array of objects like this: let sampleArrayOfObject = [{ Item: &qu ...

How can you selectively conceal the nth item in a v-for loop while keeping the original array intact? Discover a way to do this using the search function

I have a reference variable called foxArticles that holds a list containing 100 items. Using a v-for loop, I iterate over each value and render all 100 values on the page. <template> <div class="news_container"> <div v- ...

Ensuring a DIV remains fixed in place for a specific number of scrolls

I'm looking to create a 'Page section' that remains in place while scrolling for a specific distance and then smoothly transitions to the next section. I've attempted to implement this within the child theme without success... Any sugge ...

Angular - ngbDropdownMenu items are hidden from view, but their presence is still detectable

Hey there! I'm currently working on a school project and I'm aiming to implement a drop-down menu. Surprisingly, it's proving to be more challenging than expected. <div class="parrent"> <div class="row"> ...

"Utilizing the flex box feature in IE 10 for efficient text trunc

.container { background: tomato; width: 100px; display: flex; } .text-left { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .text-right { } <div class=container> <div class="text-left"> title title title ...

Inner-loop containing a variable limited to the block

I have a structure similar to the example below: new Vue({ el: '#app', data: { tasks: ['task1', 'task2', 'task3'], actions: { 'task1': { name: 'dig', ...

"Pairing div/span elements with sprites for improved web

I've been updating my website by replacing most inline images with sprites. Here's a basic CSS code snippet for the sprite class: .sprite{ background-image:url(...); background-position:...; width: 16px; height:16px; } After learning that nest ...

Replace the default focus state using CSS or resetting it to a custom style

I'm looking for a solution similar to a CSS reset, but specifically for the :focus state. If such a thing doesn't exist yet, I'm interested in learning about the possible properties that can be reset or overridden in order to create a new :f ...

Transform your ordinary HTML select into a stylish span with this custom CSS class

I need help making a select element appear like a span under certain conditions. The CSS class I currently have works with input boxes, but not with dropdowns. Is there any advice on how to style the select element to look like a span without actually repl ...

Utilize JavaScript conditions to dynamically apply styles within your web application

I am facing a challenge with managing two separate <style> tags that each contain a large number of styles and media queries. The issue is that one set of styles is intended for desktop users, while the other is meant for mobile users. When both se ...

On certain websites, the footer may appear distorted or incorrectly displayed when viewed in the IE 9 web browser

Currently experiencing an issue with the display of the footer in IE9. On some pages, the footer is not properly fixed and overlaps the middle of the content. However, this problem does not occur when using other web browsers. Here is the code that I am c ...