[Vuejs]-Elements disappear in the wrong order, in vue.js

0👍

You should use the <TransitionGroup> Vue component to apply transitions/animations to components. No need for you to manage the animation timing manually, the transitions will be applied to any mounting/destroying components.

Here is a working example:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title></title>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css">
  <script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
  <script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
  <script>const rid = _ => "rid_" + Array.from(crypto.getRandomValues(new BigUint64Array(2))).map(item => item.toString(36)).join("");</script>
  <script>const ider = query => "#"+$(query).last().attr(`id`, rid()).attr(`id`);</script>
  <style>
    component { display: none; }
    app { display: block; }
  </style>
</head>
<body>
<style>
  @keyframes vbst_toast-show {
    0%   { opacity: 0%;   }
    100% { opacity: 100%; }
  }
  @keyframes vbst_toast-fade {
    0%   { opacity: 100%; }
    100% { opacity: 0%;   }
  }
  .vbst_toast-show {
    animation-name: vbst_toast-show;
    animation-duration: 1s;
    animation-play-state: running;
    animation-fill-mode: forwards;
  }
  .vbst_toast-fade {
    animation-name: vbst_toast-fade;
    animation-duration: 1s;
    animation-play-state: running;
    animation-fill-mode: forwards;
  }
 .fade-move,
 .fade-enter-active,
 .fade-leave-active {
   transition: all 0.5s ease;
 }
 .fade-enter-from,
 .fade-leave-to {
   opacity: 0;
   transform: translateX(30px);
 }
 .fade-leave-active {
   position: absolute;
 }
</style>
<component name="bs-alert">
  <div :id="toast.id" class="toast show">
    <div class="toast-header">
      <i class="bi" :class="['bi-'+toast.icon,'text-'+toast.color]"></i>
      <strong class="ms-2" :class="['text-'+toast.color]">{{toast.title}}</strong>
      <small  class="ms-auto">{{toast.time}}</small>
      <button class="ms-2 btn btn-link text-danger p-0 bi bi-x"></button>
    </div>
    <div class="toast-body">{{toast.body}}</div>
  </div>
</component>
<app>
  <transition-group name="fade">
    <bs-alert 
      v-for="toast in toasts"
      :toast="toast"
      :key="toast.id"
    />
  </transition-group >
</app>
<script>
  APP$VUEBS_TOASTER = Vue.createApp({
    data() {
      return {
        title: "<%- title %>",
        toasts: []
      }
    },
    components: {
      'bs-alert': {
        props: ['toast'],
        template: ider(`component[name="bs-alert"]`),
        data() {
          return {
            animation: 'vbst_toast-show'
          }
        },
        mounted() {
          setTimeout(() => {
            this.animation = 'vbst_toast-fade';
          }, 6000);
        }
      }
    },
    methods: {
      addToast(toast) {
        this.toasts.push(toast);
        setTimeout(_=>{ this.toasts.shift() }, 3000);
      }
    }
  }).mount(ider(`app`))
</script>
<button style="position: fixed; bottom: 0; right: 0;">add toast</button>
<script>
  $("button").last().on("click", function() {
    APP$VUEBS_TOASTER.addToast({ 
      id: rid(),
      color: "primary", 
      icon: "alarm", 
      title: "title", 
      body: `hello world!`
    });
  })
</script>
</body>
</html>

Leave a comment