Philipp Mochine
Philipp Mochine

Reputation: 4705

Vue - How to render $slots.default in a div to calculate its height?

I'm trying to simplify my problem:

Let us say I have a modal component

//example.php
<modal>
  <div>A big div</div>
</modal>

Before the modal is shown I need to calculate the height for the proper animation. Inside the modal Vue it looks like this:

   //Modal.vue
   ...
   <transition
      :name="transition"
      @before-enter="beforeTransitionEnter"
      @after-leave="afterTransitionLeave"
    >
      <div
        v-if="visibility.modal"
        ref="modal"
        class="v--modal v--modal-box"
        :style="modalStyle"
      >
        <slot/>

      </div>
    </transition>

I know that with this.$slots.default I get the node of the slot. But I'm not sure how I can create a div, add the node to the div so that I can then calculate the height of it?

Edit: Is it possible to call your own render function so I can use it like in the docs?

render: function (createElement) {
  // `<div><slot></slot></div>`
  return createElement('div', this.$slots.default)
}

Like

  guessSlotsHeight(){

  let modalDiv = document.createElement('div')

  modalDiv.className = 'v--modal v--modal-box'

  //something like
  let slotDiv = this.render('div', this.$slots.default)
  modalDiv.appendChild(slotDiv);
},

Upvotes: 2

Views: 3083

Answers (1)

Vadym Semenets
Vadym Semenets

Reputation: 143

Your logic is too complex. It's easiest than you think.

Look, animation is the thing which happens to DOM. Therefore, DOM is available when animation starts. So, you can access any DOM element using Vue ref (recommended) or native javascript syntax in mounted hook. And there you can take the element height or any other property you want and store in in your data (for example).

Possible solution example #1

Vue transition before-enter event function has the target element as an argument by default. So, you can get the modal height in your beforeTransitionEnter function:

Modal.vue

data() {
   return {
      modalHeight: 0,
      visibility: {
         modal: false
      }
   }
},

methods: {
   beforeTransitionEnter(element) {
     this.modalHeight = element.offsetHeight;
   }
}

Possible solution example #2

For this example there is one important detail. I see in your code example that you display the next div conditionally:

`<div v-if="visibility.modal" class="v--modal v--modal-box">`

Be careful with that, as I don't know what is the init value of visibility.modal.

I would use watch feature to know content height every time once visibility.modal gets true in this particular case.

Look at the example. It's based on the code which you provided and it's a possible solution in your case.

Modal.vue

data() {
   return {
      isMounted: false,
      modalHeight: 0,
      visibility: {
         modal: false
      }
   }
},

mounted() {
   this.isMounted = true;
},

watch: {
  visibility: {
     handler(value) {
       if (value.modal && this.isMounted) {
         this.modalHeight = this.$refs.modal.clientHeight;
       }
     },
     deep: true
  }
}

As you can see - you will always have actual modalHeight value and you can use it whenever you want within the Modal.vue component.

Upvotes: 1

Related Questions