vue.jsvuejs3vue-composition-api

Programmatically calculate element height in vuejs 3


I have made myself a nice CarouselComponent based on bootstraps carousel, which can take children as slots and automatically creates slides for each of the children:

App.vue:

<CarouselComponent identifier="TestCarousel">
    <CarouselItemComponent someParameter="ImAParameter">
        Items can also take content in their slots.
    </CarouselItemComponent>
</CarouselComponent>

The component can take some additional parameters (e.g. enable/disable autoplay) but the core functionality just comes from finding the slot items and iterating bootstraps carousel items over them:

CarouselComponent.vue:

<script setup>
import { useSlots, computed } from 'vue'
defineProps({
  identifier: {
    type: String,
    required: true,
  },
  // other properties
})

var slots = useSlots()
const childComponents = computed(() => {
  return slots.default()
})
</script>

<template>
    <!-- standard boostrap carousel html -->
      <div
        class="carousel-item"
        :class="{ active: index === 0}"
        v-for="(child, index) in childComponents"
        :key="index"
      >
        <component :is="child" />
      </div>
    <!-- standard boostrap carousel html -->
</template>

This works fine, but I want to add a feature and have not found a good way to do it. The problem with this setup is, that I cannot know the actual height of the different slides beforehand (and enforcing fixed heights resulted in unwanted look and feel). This results the content below the carousel jumping around due to the height of the carousel being changed for each slide change

So, what I would like to do is to find out the height of the tallest slide and set the height of the carousel based on that. The question is, how can I do that without the user noticing? I think I have to actually render the slides to find the height, but can I do that in the background somehow? Or is there another (better) approach to this problem altogether?


Solution

  • There are several "classical" solutions to your problem:

    1. Probably the most popular is to contain the carousel in a section with fixed height, making sure its height is higher than the max height of the carousel (typically 100vh - menu height) = a screen height. You might want to center the carousel vertically in the section. This way, the carousel doesn't influence the vertical offset of the rest of the page
    2. Another solution is to only update the carousel's height upwards:
      • contain the carousel in a wrapper with overflow-y: hidden; max-height: 0; min-height: 0.
      • watch the carousel's height (with a resize observer) and every time it changes, update the wrapper's min and max height to the max between it's current height and the carousel's height. This way, the content below the carousel will only move downwards and only on the first pass through the carousel slides. After that, it will already be at its max value, so it won't move anymore. To avoid jumps in height, you should animate the transitions of min-height and max-height on the wrapper.
    3. And third solution is to render all slides inside a hidden element having the same width as the carousel, calculate their heights and set it as the carousel's height. Performance wise, this is the most costly. Also, you'll need to do it on each window resize, preferably debounced. Although technically the most precise, I advise avoiding it, for performance reasons.