Determining the dimensions of Vue slots

I am struggling with determining the height and width of slots to render images in my Perimeter component. The images should be 105x160 in size, but when I check clientWidth and clientHeight, I get 0x24.

My issue seems related to this problem discussed on Stack Overflow: Measuring the height of a Vue.js 2 component after slot rendering. Despite trying $nextTick on both the Perimeter component and individual slot components, I can't seem to resolve it.

In the Perimeter component code snippet below:

<template>
  <div class="d-flex">
    <slot></slot>
    <div class="align-self-center">
      <slot name="center-piece"></slot>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Perimeter',
    mounted() {
      this.distributeSlots();
    },
    updated() {
      this.distributeSlots();
    },
    computed: {
      centerRadius() {
        return this.$slots['center-piece'][0].elm.clientWidth / 2;
      },
    },
    methods: {
      distributeSlots() {
        let angle = 0;
        const {
          clientHeight: componentHeight,
          clientWidth: componentWidth,
          offsetTop: componentOffsetTop,
          offsetLeft: componentOffsetLeft,
        } = this.$el;
        const componentXCenter = componentWidth / 2;
        const componentYCenter = componentHeight / 2;

        const slots = this.$slots.default.filter(slot => slot.tag) || [];
        const step = (2 * Math.PI) / slots.length;

        slots.forEach((slot) => {
          slot.context.$nextTick(() => {
            const { height, width } = slot.elm.getBoundingClientRect();
            console.log(`height ${height}, width ${width}`);
            const distanceFromCenterX = (this.centerRadius + componentXCenter) * Math.cos(angle);
            const distanceFromCenterY = (this.centerRadius + componentYCenter) * Math.sin(angle);
            const x = Math.round((componentXCenter + distanceFromCenterX + componentOffsetLeft) - (width / 2));
            const y = Math.round((componentYCenter + distanceFromCenterY + componentOffsetTop) - (height / 2));

            slot.elm.style.left = `${x}px`;
            slot.elm.style.top = `${y}px`;

            angle += step;
          });
        });
      },
    },
  };
</script>

Initially, my `distributeSlots()` method did not have `$nextTick`:

distributeSlots() {
  let angle = 0;
  const {
    clientHeight: componentHeight,
    clientWidth: componentWidth,
    offsetTop: componentOffsetTop,
    offsetLeft: componentOffsetLeft,
  } = this.$el;
  const componentXCenter = componentWidth / 2;
  const componentYCenter = componentHeight / 2;

  const slots = this.$slots.default.filter(slot => slot.tag) || [];
  const step = (2 * Math.PI) / slots.length;

  slots.forEach((slot) => {
    const { height, width } = slot.elm.getBoundingClientRect();
    const distanceFromCenterX = (this.centerRadius + componentXCenter) * Math.cos(angle);
    const distanceFromCenterY = (this.centerRadius + componentYCenter) * Math.sin(angle);
    const x = Math.round((componentXCenter + distanceFromCenterX + componentOffsetLeft) - (width / 2));
    const y = Math.round((componentYCenter + distanceFromCenterY + componentOffsetTop) - (height / 2));

    slot.elm.style.left = `${x}px`;
    slot.elm.style.top = `${y}px`;

    angle += step;
  });
},

This is how I'm passing the Perimeter component:

<template>
  <perimeter>
    <div v-for="(book, index) in books.slice(0, 6)" v-if="book.image" :key="book.asin" style="position: absolute">
      <router-link :to="{ name: 'books', params: { isbn: book.isbn }}">
        <img :src="book.image" />
      </router-link>
    </div>
  <perimeter>
</template>

Oddly enough, when I log `slot.elm` during the forEach loop and inspect the array in the browser console, I see correct clientHeight and clientWidth values:

https://i.sstatic.net/ogIPN.png

Answer №1

Typically, in situations like this, the issue lies in a logical error rather than a problem with the framework. To troubleshoot, try simplifying your code to the most basic form that illustrates the problem.

If you are obtaining clientWidth and clientHeight either on mounted() or later, as shown below, it should function correctly.

Avoid using timer workarounds as they can lead to difficult-to-trace bugs.

<template>
  <div style="min-height: 100px; min-width: 100px;">
    <slot />
  </div>
</template>

<script>
export default {
  name: 'MyContainer',
  data (){
    return {
      width: 0,
      height: 0,
    }
  },
  mounted (){
    this.width = this.$slots["default"][0].elm.clientWidth
    this.height = this.$slots["default"][0].elm.clientHeight
    console.log(this.width, this.height)  // => 100 100 (or more)
  },

}
</script>

<style scoped lang="scss">
</style>

Answer №2

A clever workaround is to utilize a trick by placing the code inside a setTimeout function to run in a separate thread with a slight delay:

setTimeout(() => {
     // Your code goes here
     ...
}, 80)

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

What is the best way to send a string parameter from an Angular UI to a Node.js backend?

My goal is to transfer a string value from an Angular UI to a Node.js backend API, which will then search in MongoDB using the provided string value as shown below. I am attempting to receive input in enteredValue and pass it on to the http.get call as pa ...

Communication between React.js and Node.js in VS Code

I recently started learning React and node js. I managed to create a simple "Hello World" project in reactjs, which works perfectly. App.js import React, { Component } from 'react'; import logo from './logo.svg'; import './App.cs ...

Identify the row containing a value of null using jQuery (functionality not performing as anticipated)

Whenever the user clicks on the GetData button, I retrieve JSON data and display it in an HTML table similar to the demo below. Demo: https://plnkr.co/edit/I4XYY6CZohf7IS6wP8dR?p=preview There are instances where the value can be null, such as the loanNu ...

When the dependency value transitions from 1 to 0, useEffect fails to trigger

I'm really puzzled by how useEffect behaves in this scenario: Check out this code snippet: const numVertices = selectionProvider.verticesSelectionProvider.count; console.log('RENDER ---> COUNT = ', numVertices); useEffect(() => { ...

What is the best way to obtain the output of the MULTIPLO.SUPERIOR function using JavaScript?

In Microsoft Excel, there is a function called upper multiple where we can input a number and it will round up to the nearest multiple of a second specified number - for example: 10,986 ; 5 = 15 105,32 ; 5 = 110 ...

Tips for making Google search results include query strings in the returned links

I need help figuring out how to make Google search results show a URL containing a query string. Here's an example from the project I am currently working on: Instead of this link, Google search returns: If anyone has any suggestions for fixing this ...

Missing Ajax Functionality in Laravel Application

The code snippet below was created by me... <script> $('#spielAuswahl').on('change', function(e){ console.log(e); var spielID = e.target.value; //ajax $get.('/spieler?spielID=' + sp ...

Utilizing the power of Angular 4 in combination with mailgun

I need assistance with setting up an email form on my website using Angular 4 and Mailgun as the mail service. I have a method in my mail service file to send messages, but I keep encountering a Bad Request error stating that 'from' is not presen ...

Ensure that a Vue component is able to verify whether a Vuex store state property contains any existing data

My current setup involves fetching the state "categories" asynchronously from a JSON endpoint. However, whenever I reload the page, the categories always appear empty when I try to work with this data in the component. methods: { onSubmit() { ...

Tips for showing and modifying value in SelectField component in React Native

At the moment, I have two select fields for Language and Currency. Both of these fields are populated dynamically with values, but now I need to update the selected value upon changing it and pressing a button that triggers an onClick function to update th ...

Tips for avoiding repeated Modal Popup instances and preventing the page from automatically scrolling to the last element when using ReactJS

I've been working on a project where I'm fetching data from a server and displaying 10 different sets of data in Bootstrap cards using the map() function. Each card contains a button to open a modal, along with a Link that shows the route related ...

What is the simplest way to extract only the error message?

Having this code snippet. $('div#create_result').text(XMLHttpRequest.responseText); If we look at the content of XMLHttpRequest, it shows: responseText: Content-Type: application/json; charset=utf-8 {"error" : "User sdf doesn't exist"} st ...

Trouble arises when implementing personalized buttons on the Slick JS slider

Are you struggling to customize buttons for your Slick Slider JS? I am facing a similar issue with applying my own button styles to the slider. I am interested in using arrow icons instead of the default buttons. Here is the HTML code snippet: <secti ...

When attempting to upload a file using Form, it only allows for a single upload of that file and will not work when attempting to upload the same file again

After selecting a file from the file chooser and uploading it, I encounter an issue when attempting to submit the form with the same file again. The submission doesn't seem to trigger any action. If I select a file, upload it, then choose the same fi ...

If I do not utilize v-model within computed, then computed will not provide a value

I'm fairly new to coding in JS and Vue.js. I've been attempting to create a dynamic search input feature that filters an array of objects fetched from my API based on user input. The strange issue I'm coming across is that the computed metho ...

The visibility of the Google +1 button is lost during the partial postback process in ASP.NET

When trying to implement the Google Plus One button using AddThis on one of our localized pages, we encountered a strange issue. Despite retrieving data from the backend (let's assume a database), the plus button was not loading during an AJAX based p ...

There appears to be an issue with the compilation of the TypeScript "import { myVar }" syntax in a Node/CommonJS/ES5 application

In my Node application, I have a configuration file that exports some settings as an object like this: // config.js export var config = { serverPort: 8080 } Within another module, I import these settings and try to access the serverPort property: // ...

Encountering difficulties during the migration process from a JavaScript to a TypeScript React Component

I've encountered some challenges with configuring TypeScript in my project. Initially, I developed my application using plain JavaScript. However, eager to learn TypeScript, I decided to convert my JavaScript project into a TypeScript one. To achiev ...

Exploring the concepts of express post and delete responses that are unclear

It appears that I am facing an issue where trying to access an attribute of an element from a JSON file returns null. Additionally, I am still encountering npm audit problems. What are your thoughts on this situation? Below is the code snippet that has be ...

What is the best way to locate and access a JSON file that is relative to the module I am currently working

I am in the process of creating a package named PackageA, which includes a function called parseJson. This function is designed to accept a file path pointing to a JSON file that needs to be parsed. Now, in another package - PackageB, I would like to invok ...