Looking to display an element right away, like a loading spinner, in Vue? If nextTick isn't doing the trick, here's what you can try

Imagine having a Vue component stored in a .vue file with a data member called isLoading: false. The template includes:

<div v-show="isLoading" id="hey" ref="hey">Loading...</div>
<button @click="loadIt()">Load it</button>

Along with a method:

loadIt() {
  this.isLoading = true
  this.$nextTick(() => {
    console.log(this.$refs.hey)
    // ...other work here that causes other DOM changes
    this.isLoading = false
  })
}

(The term "Loading" refers to loading from the local memory, not an external request. The goal is to display a simple "loading" message instantly while the potential 0.2-0.5 second DOM modifications are underway.)

The expectation was that the $nextTick function would update both the virtual and actual DOM. Despite the console log indicating that the item was displayed (removing the display: none style), the "Loading..." indicator never appears in Chrome or Firefox. The brief pause occurs, followed by the subsequent DOM changes without displaying the Loading indicator.

If opting for setTimeout instead of $nextTick, the loading indicator becomes visible, but only when the additional tasks run slowly enough. When there is a delay of several tenths of a second, the loading indicator remains hidden. The desired outcome is for it to show promptly upon clicking for a more responsive user interface.

Check out the example on JSFiddle

Answer №1

As per findings on this Github 'issue', it seems that there is some unusual behavior with the Vue.nextTick function. The nextTick function apparently executes just before the DOM is due to be re-rendered. This can result in the loading div being hidden right after it briefly appears due to the timing of the nextTick. Although suggestions were provided on the Github issue, not all of them proved effective.

The workaround reported to make it function properly involves using setTimeout along with a specified delay. While a 1 millisecond delay may not consistently trigger a DOM update, a delay of around 25 milliseconds is recommended for better results. Although not an ideal solution, this workaround was the most reliable option discovered. Hopefully, you may find the link to the Github discussion helpful for further insights.

JSFiddle showcasing examples

Answer №2

It seems like you might be overthinking things.

Here is a simplified version that should work smoothly:

new Vue({
  el: "#app",
  data: {
   isLoading: false,
   done: false
  },
  methods: {
    loadIt() {
      this.isLoading = true
      // Simulating a lengthy AJAX request...
      window.setTimeout(() => {
        this.done = true
        this.isLoading = false
      }, 3000)
    }
  }
})

Update: After further investigation, I found a workaround for your issue. It's not the most elegant solution but it seemed to resolve the problem related to the event loop.

new Vue({
  el: "#app",
  data: {
   isLoading: false,
   done: false
  },
  methods: {
    loadIt () {
      this.isLoading = true

      window.setTimeout(() => {
        for (var i = 1; i < 40000000; ++i) {
          this.done = !i
        }

        this.done = true
        this.isLoading = false
      }, 10)    
    }
  }
})

Note: This solution worked perfectly for me, although I did notice some quirks with saving and running on JSFiddle. Remember to save by pressing Command / Ctrl + S before clicking the Run button.

Answer №3

Issue stems from the behavior of browsers themselves. They tend to have a lazy approach to re-rendering in order to prevent layout thrashing. To overcome this, you can utilize a simple trick to prompt the browser to immediately re-render the screen:

loadIt() {
  this.isLoading = true
  this.$nextTick(() => {
    // At this point, the reference is available
    console.log(this.$refs.hey)
    
    // The problem arises here. The browser waits for further DOM changes
    // to batch them and compute all changes at once, causing your component
    // to not render when changing visibility back to invisible state.
    // To resolve this issue, force the browser to re-render by, for example,
    // requesting the new element width:
    let width = this.$refs.hey.width
    
    // The browser has no choice but to re-render the DOM and provide the new
    // element width. Your component is now rendered by the browser and you can
    // safely switch the visibility again:
    this.isLoading = false
  })
}

Full example:

<template>
  <div v-if="isLoading" ref="hey">
    Loading...
  </div>

  <button @click="loadIt">
    Load it
  </button>

  <div v-if="isDone">
    Done
  </div>
</template>

<script>
export default {
  name: 'MyComponent',

  data () {
    return {
      isLoading: false,
      isDone: false
    }
  },

  methods: {
    delay: ms => new Promise(res => {
      setTimeout(res, ms)
    }),

    async loadIt() {
      this.isLoading = true

      // wait for virtual DOM to render to real DOM
      await this.$nextTick()

      // force DOM re-render
      console.log(this.$refs.hey.width)

      // wait for some time, longer than monitor refresh rate
      await this.delay(40)

      this.isDone = true
      this.isLoading = false
    }
  }
</script>

Answer №4

The reason for this issue is the usage of synchronous JavaScript operations.

for (var i = 1; i<10000000; ++i){
    this.done = !i
}

To display the loading indicator on the page, it's necessary to either utilize a web worker or switch the logic to an asynchronous style.

Check out this working example on fiddle: https://jsfiddle.net/508odkm9/1/

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

Transforming a JavaScript component based on classes into a TypeScript component by employing dynamic prop destructuring

My current setup involves a class based component that looks like this class ArInput extends React.Component { render() { const { shadowless, success, error } = this.props; const inputStyles = [ styles.input, !shadowless && s ...

Automatically Dismissing a Bootstrap Alert After a Set Number of Seconds

Having some trouble closing a Bootstrap alert automatically on my new site built using the Bootstrap Framework. The issue arises when trying to close the alert after a certain amount of time. On the main page of the site, there is a modal login form. When ...

The words spill across the navigation bar

I am facing an issue with my navbar where the text overflows and creates extra space due to a white background. I have tried resolving this for a while but haven't been successful. I need help from someone knowledgeable in this area. Here are some im ...

Modify a unique custom binding handler in such a way that it is designated using an Immediately Invoked Function Expression

I am currently working on improving a custom binding handler by converting it into an Immediately Invoked Function Expression (IIFE). Despite researching IIFE online, I am still unsure of how to make the necessary changes to my custom handler. Can someon ...

C# implementation of the btoa function from JavaScript

I am in need of assistance recoding a JavaScript function to C# that makes use of the btoa method to convert a string of Unicode characters into base64. The challenge lies in ensuring that the encoding used in both languages is identical, as currently, the ...

A guide on extracting JSON data from a script tag using Cheerio

I am in the process of extracting metadata from various websites. While utilizing Cheerio to retrieve elements like $('meta[property="article:published_time"]').attr('content') works smoothly for most sites, there are some whe ...

Exploring the Use of 7BitEncodedInt in JavaScript

Currently, I am trying to read a binary file using JavaScript. It appears that this file may have been written in C#, which handles strings differently from how it's done in the source mentioned at https://learn.microsoft.com/en-us/dotnet/api/system. ...

The AJAX request to fetch XML data resulted in a failure to redirect as intended

I'm encountering an issue with redirection after retrieving the XML data. The scenario involves a login process that fetches the ID values for username and password, verifies their existence, and then displays the alert "worked." However, despite show ...

The POST request is not functioning. An error has occurred: Unable to retrieve response data - no content is available for the preflight request

Having trouble making a post request from my client side to the back-end. Even with the new movie hardcoded, it keeps returning an error stating that there was a failure in loading the response data. Below is the code for the front-end: //Fetch request t ...

Switching between components in vue.js: A beginner's guide

In my project, I created a Dashboard.vue page that consists of three child components: Display, sortBooksLowtoHigh, and sortBooksHightoLow. The Dashboard component also includes a select option with two choices: "Price: High to Low" and "Price: Low to High ...

Looking to confirm client-side text in NodeJS?

As I work on constructing a to-do list, one challenge I am encountering is confirming that the correct task has been checked off. While considering using unique IDs for each individual task may seem like a solution, there is still the risk of users manipul ...

Error animation on client-side validation not resetting correctly

Incorporated a form validation and error display system utilizing TransitionGroup for animations. The flag issueVisible manages the visibility of the error message, while determineField() aids in identifying the field related to the error. The issue arise ...

The issue of infinite rendering caused by useState and how to effectively resolve it

I'm facing a strange issue in just a few lines of code, and I can't quite figure out what's happening behind the scenes. Here are the 4 lines causing trouble: function FarmerComponent(props) { let authCtx = useContext(AuthContext) let u ...

"Having trouble getting the onChange function to work with Material-UI's Select

I have encountered an issue while trying to implement the material-ui SelectField component in my project. While the common select component works seamlessly, I face problems when using the SelectField component. The main problem arises with the invocati ...

The functionality of Environment Variables only applies during the build process and does not affect the execution of npm run serve

After running into a problem with my environment variables, I discovered that they work smoothly with npm run build, but not with vue-cli-service serve --mode sandbox To troubleshoot, I referred to the Vue documentation here https://cli.vuejs.org/guide/c ...

Unable to load models in face-api.js even though attempting to infer the models

Currently, I am working on developing an application for face detection. However, I am encountering an error stating "SsdMobilenetv1 - load model before inference" despite loading the models. The Front End HTML File is being sent from the server, ...

Achieving the highest ranking for Kendo chart series item labels

Currently, I am working with a Kendo column chart that has multiple series per category. My goal is to position Kendo chart series item labels on top regardless of their value. By default, these labels are placed at the end of each chart item, appearing o ...

Concealing a feature across all pages of the website

I have implemented alert boxes that appear on all pages of the website. Users can individually close each alert box: $(document).ready(function() { $("#close-alert-box-news").click(function() { $("#alert-box-news").hide(800); }); $("#close-al ...

Tips for identifying and handling a 400 bad request error in an HTTP response within an Angular 2 application

I attempted to handle the error 400 bad request in this manner: catch((error: any) => { if (error.status === 500) { return Observable.throw(new Error(error.status)); } else if (error.status === 400) { console.log( 'err ...

Performing an API request and saving the response data into a

I’m struggling to figure out how to make an API call with request in MongoDB using Mongoose. I’m new to this topic and could really use some help! Here is my test.js file, does anyone have any ideas on how to solve this issue? My goal is to save the AP ...