Implementing dynamic row transitions with Vue.js on tables

I'm struggling to implement a transition effect on an HTML table row using Vue.js. Here is the complete example:

  new Vue({
    el: '#data',
    data: {
      items: [
        {
          data: 'd1',
          more: false
        },
        {
          data: 'd2',
          more: false
        },
      ]
    }

  });
.fade-enter-active, .fade-leave-active {
        transition: opacity 2s
      }
      .fade-enter, .fade-leave-to  {
        opacity: 0
      }
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>


    <div class="container-fluid" id="data">
      <br>
      <br>
      <table border="1" class="table table-bordered">
        <thead class="thead-inverse">
          <tr>
            <th>anim</th>
          </tr>
        </thead>
        <tbody>  
        <template v-for="item, k in items">
          <tr>
            <td><button @click="item.more = !item.more" type="button" 
                         v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']" class="btn">Show the hidden row</button></td>
          </tr>

          <transition name="fade"> 
            <tr v-bind:key="item" v-if="item.more">
              <td><p>{{k + 1}} - {{item.data}}</p></td>
            </tr>
          </transition>

        </template>
        </tbody>
      </table>
    </div>

I am trying to achieve a fade-in effect on a hidden table row when it appears, but nothing seems to be happening. Do you have any suggestions on how I can achieve this? It works perfectly fine on other elements like span.

Answer №1

First and foremost, I'd like to mention that if you had utilized a string template, the code in your query would have functioned perfectly as is.

console.clear()
new Vue({
    el: '#data',
    template: `
     <div>
          <br>
      <br>
      <table border="1" class="table table-bordered">
        <thead class="thead-inverse">
          <tr>
            <th>anim</th>
          </tr>
        </thead>
        <tbody>  
        <template v-for="item, k in items">
          <tr>
            <td><button @click="item.more = !item.more" type="button" 
                         v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']" class="btn">Show the hidden row</button></td>
          </tr>

          <transition name="fade" > 
            <tr  v-bind:key="item" v-if="item.more">
              <td><p >{{k + 1}} - {{item.data}}</p></td>
            </tr>
          </transition>

        </template>
        </tbody>
      </table>
      </div>
    `,
    data: {
      items: [
        {
          data: 'd1',
          more: false
        },
        {
          data: 'd2',
          more: false
        },
      ]
    }

  });
.fade-enter-active, .fade-leave-active {
        transition: opacity 2s
      }
      .fade-enter, .fade-leave-to  {
        opacity: 0
      }
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>


    <div class="container-fluid" id="data">

    </div>

In this example, the only modification made was converting the Vue template into a string instead of declaring it in the DOM. The reason why this alteration works is because when using an "in DOM" template, the browser interprets the HTML before Vue transforms the template into a render function. Browsers are particular about which elements can be rendered inside a table, primarily only elements related to tables such as thead, tbody, tr, td, etc. It seems that browsers do not handle the transition element well (although interestingly, they don't encounter issues with template).

On the other hand, string templates bypass browser parsing and are directly converted into render functions, ensuring they work as intended. So, my suggestion would be to opt for a string template.

If you prefer to continue using an in DOM template, certain modifications are necessary. Firstly, we need to relocate the transition to a suitable location where the browser will accept it. For a table, we can accomplish this by moving it to the tbody tag and utilizing Vue's special is directive. Also, since our transition now applies to multiple elements, transitioning to a transition-group is essential.

Given that we're implementing a transition-group, every element within the transition must possess a key. Hence, for each row, simply add a unique key.

console.clear()
new Vue({
  el: '#data',
  data: {
    items: [{
        data: 'd1',
        more: false
      },
      {
        data: 'd2',
        more: false
      },
    ]
  }

});
.fade-enter-active,
.fade-leave-active {
  transition: opacity 2s
}

.fade-enter,
.fade-leave-to {
  opacity: 0
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script>

<div class="container-fluid" id="data">
  <br>
  <br>
  <table border="1" class="table table-bordered">
    <thead class="thead-inverse">
      <tr>
        <th>anim</th>
      </tr>
    </thead>
    <tbody  name="fade" is="transition-group">
      <template v-for="item, k in items">
          <tr v-bind:key="`button-${item.data}`">
            <td>
              <button @click="item.more = !item.more" 
                      type="button" 
                      v-bind:class="[item.more ? 'btn-danger' : 'btn-primary']" 
                      class="btn">Show the hidden row
              </button>
            </td>
          </tr>
          <tr  v-bind:key="`detail-${item.data}`" v-if="item.more">
            <td><p >{{k + 1}} - {{item.data}}</p></td>
          </tr>
        </template>
    </tbody>
  </table>
</div>

Answer №2

Instead of using the <transition> tag, try this alternative approach:

 <tr name="fade" is="transition" v-bind:key="item.data" v-if="item.more">

Source: https://github.com/vuejs/vue/issues/3907#issuecomment-253111682

Fiddle: https://jsfiddle.net/c8vqajb4/3/

UPDATE:

We decided to implement a transition-group instead:

<tbody name="fade" is="transition-group">
    <tr class="row" v-bind:key="item.data" v-if="item.more">
      <td><p >{{k + 1}} - {{item.data}}</p></td>
     </tr>
</tbody>

This was based on the suggestion provided here: https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions

Fiddle: https://jsfiddle.net/c8vqajb4/4/

Answer №3

transitions are only effective on a single rendered element

when transition is placed within the tag, it functions properly

consider utilizing transition-group for additional assistance

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

In ReactJS, ensure only a single div is active at any given moment

I'm working on a layout with three divs in each row, and there are multiple rows. Only one div can be selected at a time within a row, and selecting a new div will automatically unselect the previously selected one. Here is a simplified version of my ...

Tips for modifying a state array in Vuex

I have the ability to add and remove entries, but I'm struggling with editing. How can I achieve this using VUEJS, VUEX, and JavaScript? For adding entries, I know I should use PUSH, and for removing entries, SPLICE works fine. But what about editing ...

Prevent the page from refreshing when a value is entered

I currently have a table embedded within an HTML form that serves multiple purposes. The first column in the table displays data retrieved from a web server, while the second column allows for modifying the values before submitting them back to the server. ...

What is the best way to implement a conditional data-attribute tag in order to trigger JavaScript functionality?

Is there any way to conditionally apply JavaScript or jQuery code in an HTML Template? For example, if a data-id attribute with the value "no-copy" is found inside the body tag, certain JavaScript code will be applied. To explain it more simply, I have J ...

Getting all inline styles from an HTML string using JavaScript

(JavaScript) I am working with an html email template stored as a string. Is there a way to extract all the inline styles used in the template? For instance, if I have this input: <div style="min-width:300px;max-width:600px;overflow-wrap:break-word ...

Having difficulty getting my create-react-app to display on Heroku

I successfully managed to get my react-app running smoothly on my localhost server. However, when I attempted to deploy it on Heroku, I encountered a problem. Upon visiting the app address provided by Heroku, all I see is a blank page with none of the comp ...

`Uniform background color across all pages`

My goal is to allow customers to select a color that will persist across different pages on the website. The code below shows how users can choose a color on the first page: <select id="color" style="width: 5%; height: 10%" size="5"> ...

Exploring AngularJS 1.x: Understanding the differences between isolated scope and using require with ngModel

Throughout my experience with Angular 1.x, I have always used isolated scope in my directives. However, recently I encountered a directive that solely utilized ngModel. This made me curious about the differences and potential issues when using both methods ...

How to extract parameters from an http get request in Node.js

Trying to handle an HTTP request in this format: GET http://1.2.3.4/status?userID=1234 I am unable to extract the parameter userID from it. Despite using Express, I am facing difficulties. Even when attempting something like the following, it does not yi ...

JavaScript disrupting CSS animations

I've just embarked on creating a simple landing-page website. One component of the page is a basic image slider with navigation controls powered by JavaScript. I managed to get everything functioning except for achieving a smooth transition between im ...

unexpected alteration of text sizing in mathjax within reveal.js presentations

Something strange is happening with the font size in my slides. The code for each slide is the same, but there is an unexpected change between the 3rd and 4th slide. I cannot figure out what is causing this discrepancy. Oddly enough, when I remove the tit ...

Updating JSON objects in jQuery with string keys

I have an array variable containing JSON data and I need to update specific values within the array using string keys. Here is a snippet of what my array looks like: { "all": [ { "image":{ "URL":"img/img1.jpeg", ...

Using VueJs to invoke a plugin from a .js file

I'm seeking a deeper understanding of working with vueJS. My current setup In my Login.vue component, there is a function logUser() from UserActions.js which in turn calls the postRequest() function from AxiosFacade.js Additionally, I use a plugin ...

On mobile devices, the code "location.assign(url)" may occasionally redirect to an incorrect URL, despite functioning correctly in the majority of instances

After setting up a page with a timeout that should automatically redirect to a specific URL after 60 minutes, I encountered an issue where the redirection sometimes leads to a loss of parameters in the URL. The JavaScript code added for this purpose is set ...

The concept of JavaScript variable scope

I am working on a script that has a structure similar to this $(function(){ var someVariable; function doSomething(){ //need to figure out how to access someVariable here } $('#something').click(function(){ //need to figur ...

Guide on converting a unique JSON structure into a JavaScript object

I've been on the hunt for a solution to this unique format challenge, but have hit a dead end so far. The issue at hand is that I'm dealing with a JSON format that doesn't play nicely with mongoDB. My goal is to convert the JSON data into a ...

Error occurs when attempting to access window.google in Next.js due to a TypeError

I've been working on integrating the Google Sign In feature into my Next app. Here's how I approached it. In _document.js import React from 'react'; import Document, {Html, Head, Main, NextScript } from 'next/document'; expo ...

Switching the Date Property of a Mongo Document using Just One Query

Is there a simple way to update a single document in Mongo using just the _id? I believe the answer is "no", but I'm curious to know if there is a method that allows for this. Target a document by _id (single document). If the readAt field exists on ...

Building a dynamic URL in ReactJS from scratch

const selectedFilters = { category: "", mealtype: "lunch", cuisinetype: "Italian", dishType: "Pasta" } const apiUrl = `https://api.edamam.com/api/recipes/v2?type=public&q=${query}&app_id=${app_id}&app_key=${app_key}`; User ...

Troubleshooting problem with Electron and sqlite3 post application packaging

I've been facing various challenges with Node and databases lately, hence the numerous questions I've posted here recently. Here's some background: I have an Electron app with an AngularJS frontend. On the electron side, I run an express s ...