Tips for stopping a CSS animation from restarting when the order of a v-for rendered list is updated in Vue.js

I am working on a project that involves creating a list of data and using a template to draw blocks with time bars for each item in the list.

The order of the items in the list can be updated at any time. However, I have noticed that whenever the list order is updated, some of the time bar animations are unexpectedly restarted.

I have created a jsFiddle to simulate this behavior. Can someone please suggest a solution to prevent this issue from happening?

JsFiddle Code:

https://jsfiddle.net/tklfiddle/30z4km8f/7/

Here is the Vue Template:

<div id="app">
  <h2>TimeBars:</h2>
  <button @click="reOrder">
    Shuffle
  </button>
  <ul class="listWrapper">
    <li v-for="(item, index) in list" class="row itemTest" :id="item.id" :key="item.id">
      <div class="timeBarContainer">
        <div class="timeBar" :style="{animationDuration: item.duration+ 's'}">
        </div>
        <div class="timeBarText">
          {{item.id}}
        </div>
      </div>
    </li>
  </ul>
  {{list}}
</div>

JavaScript:

new Vue({
  el: "#app",
  data: {
    list: [
      { id: "bar1", done: false, duration: 10 },
      { id: "bar2", done: false, duration: 10 },
      { id: "bar3", done: true, duration: 10 },
      { id: "bar4", done: true, duration: 10 }
    ]
  },
  mounted(){
        console.log("DEBUG: mounted", $("body"));
    $(".listWrapper").on('DOMNodeRemoved', function(e) {
      console.log(e.target, ' was removed');
    });
  },
  methods: {
    reOrder: function(){
        this.list = shuffle(this.list);
      //this.list.pop();
      console.log("DEBUG: this.list:", JSON.stringify(this.list));
      function shuffle(array) {
          var currentIndex = array.length,  randomIndex;
          // While there remain elements to shuffle...
          while (0 !== currentIndex) {
            // Pick a remaining element...
            randomIndex = Math.floor(Math.random() * currentIndex);
            currentIndex--;
            // And swap it with the current element.
            [array[currentIndex], array[randomIndex]] = [
              array[randomIndex], array[currentIndex]];
          }

          return JSON.parse(JSON.stringify(array));
        }
      }
    }
})

CSS:

body {
  background: #20262E;
  padding: 20px;
  font-family: Helvetica;
}

#app {
  background: #fff;
  border-radius: 4px;
  padding: 20px;
  transition: all 0.2s;
}

li {
  margin: 8px 0;
  >div{
    display: inline-block;
  }
}

h2 {
  font-weight: bold;
  margin-bottom: 15px;
}

.timeBarContainer{
  width: 100%;
  height: 15px;
  position: relative;
}

.timeBar{
  width: 100%;
  height: 100%;
  background-color: red;
  float: right;
  animation: timeBarAnimation 0s linear normal forwards;
}
.timeBarText{
  position: absolute;
}

@keyframes timeBarAnimation {
    0% {
        width: 100%;
    }
    100% {
        width: 0;
    }
}

CodeSandBox Code:

If you want to see another example coded with CodeSandbox, check out this one. You will notice that the TimeBar component doesn't get unmounted after being shuffled, but the CSS animation restarts unexpectedly.

https://codesandbox.io/s/great-smoke-n806b?file=/src/components/TimeBar.vue

Answer №1

After encountering the same issue, I devised a creative (albeit specific) solution:

  • Create a fixed-length array filled with empty objects that have properties like active: false and additional data properties such as my-custom-content = ''
  • Generate this empty list early on, perhaps in the mounted hook (depending on your scenario)
  • Use a v-for loop on this array, but only render each object when it is active (utilize v-if=object.active within a child element of your v-for or opt for a filtered list)
  • If you need to display something, directly alter a specific property of a particular object in the array. Avoid using push or splice.
  • Vue does not treat this property alteration as a change to the entire list, so the v-for loop is not re-rendered, thereby maintaining CSS animations without resetting (even though the data is dynamically modified)

This method may seem unconventional, as it essentially requires reinventing list manipulation for more complex requirements, which can be cumbersome. Nevertheless, for simpler scenarios like mine, it proved effective and might do the trick for yours too :)

Answer №2

Check out this helpful resource:

How to Properly Update Arrays and Objects in Vue.js

It's important to note that when making changes to an Array in Vue.js, you should avoid directly setting values at specific indices or modifying the length property. Instead, make use of Vue's array instance methods or completely replace the array. Vue offers a convenient method arr.$set(index, value) as a shorthand for arr.splice(index, 1, value).

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

Challenges arise when integrating ng-model with Angular Chosen

I'm working with a table that lists users, each row ending with a button that triggers a popup form. Inside the popup, I'm using a multiple select feature with Angular Chosen to display each user's 'interests'. However, despite fet ...

Arranging an Array of Objects in JavaScript by dual criteria

In my JavaScript code, I have an object structured like this: myArray[0] -> 0:"62", 1:8, 2:0, 3:"11" myArray[1] -> 0:"62", 1:8, 2:0, 3:"15" myArray[2] -> 0:"48", 1:8, 2:0, 3:"04" myArray[3] -> 0:"48", 1:8, 2:0, 3:"01" myArray[4] -> 0:"62", ...

Managing rows in the Vuetify grid system

I am currently working on rearranging the layout of rows in specific columns. My goal is illustrated in the attached image - I want the red card item to span across the row it is placed in, but I am unsure how to achieve this. Additionally, I would like th ...

Guidelines for callbacks and the impact on scope

I am currently diving into the world of scopes in angularjs, specifically when it involves calling callbacks on the module utilizing a directive. I have discovered three different methods to achieve the same goal and I am attempting to comprehend the advan ...

What is the best way to choose CSS class attributes using Mootools and the getStyle()

Seeking to duplicate an object, I am trying to figure out how to retrieve class CSS attributes from Mootools. css: .card { width: 109px; height: 145px; } html: <div id="cards"> <div class="card" id="c0"> <div class="face fron ...

Tips on choosing and showcasing information from jQuery date and time picker

I am struggling to show the selected data from a jQuery date and time picker and save it to a database using PHP with MySQL. I am not sure how to retrieve the information. Here is the jQuery code for the date and time picker along with a suggested jQuery f ...

Organizing folders and files for Nuxt query string parameters

What is the optimal structure for organizing files and folders to utilize URL query string parameters instead of regular parameters? For example: Using URL parameters: Folder organization: pages/ ---|comments/ ------|_id.vue This setup leads to the fol ...

Show or hide a component based on a mouse click in Vue JS

After a prolonged absence from working with Vue JS, I find myself in the process of displaying a list of data with a button for each item. The goal is to conditionally render a component when a button is clicked. I am wondering if there is a recommended a ...

Tips for positioning an inline label and input field in an Angular application using CSS and HTML: How to align the label to the left and the input

I'm currently developing an Angular form with multiple label-input field combinations. I have managed to style the labels and input fields with inline block so that they appear on the same row. However, I am facing a challenge in aligning the label to ...

Tips for transferring and retrieving information with an express backend server

Here is the front-end code snippet: export default async function get(){ let res = await fetch('http://localhost:4000/data/'); console.log(res.json()); } And this is the back-end code snippet: const scraper = require('./scraper.js&a ...

I am experiencing difficulties with my WordPress page and posts not loading properly on the webpage

After successfully converting my HTML template to WordPress by updating all the extensions to PHP, I noticed that all my template parts are functioning correctly. However, I am facing an issue where my WordPress posts and pages are not loading on the web ...

Sending a tailored query string through a form

Currently, when I submit a form, it directs me to the URL www.domain.com/search/?maxprice=10000000. However, I want it to redirect me to a custom URL such as www.domain.com/search/maxprice_10000000/ I came across some JavaScript code that was supposed to ...

Unable to load the content within the iFrame

While attempting to retrieve the URL of an older website, I encountered an error: Fetch API cannot load http://xyz. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://abc' is therefore not allo ...

When using angularjs, the $window.location.href may cause the page to load without any

I have a dilemma where I have linked all my CSS and JS files in the index.html file, but subpages are located in a templates directory. When using $window.location.href, only a plain HTML page is returned without any CSS styles. The page renders fine when ...

Increasing the value of a food topping element within a v-for list of toppings when clicking on the "+ add" button in Vue

I'm a newcomer to the world of JavaScript and Vue.js, currently working on a project to automate the ordering process for my pizza delivery pizzeria. On the website, I have a list of toppings that customers can choose from. They have the option to se ...

The media query for mobile is not functioning properly

Hello, I have a page with MVC that includes CSS for iPad and mobile devices. The media query for iPad (@media only screen and (min-device-width: 768px) and (max-device-width: 1024px)) works perfectly, but I am struggling to get the media query for mobile ...

Playback on iPhone devices and Safari experiences a 50% reduction with AudioWorklet

I recently developed a basic audio recorder that utilizes the AudioWorkletAPI. While the playback functions smoothly on Chrome, it seems to have issues on Safari and iPhone devices (including Chrome on iPhone) where half of the audio is missing. Specifical ...

The Google Maps application is having trouble zooming in

I'm having trouble with my function that takes the zoom level and sets the center of the map. When I call setCenter with my positional coordinates and zoom level, the map doesn't zoom in where I want it to. However, the position of my info window ...

I need to find a way to position my login in the center of the page, even if my HTML body doesn't take up the entire space. This is

I am having trouble centering my login Component in the red zone on the page. It seems that my html and body tags are not taking up all the available space on the page. If you want to view my project, you can find it on my GitHub at https://github.com/SIGX ...

Watching mov and mp4 files directly in your web browser

I am currently experimenting with the HTML5 video tag and database video files. In my database, I have several records that contain both mov and mp4 files. While HTML5 video tag supports mp4 files, it's a different story when it comes to mov files. I& ...