Run multiple animations in sequence in jQuery without polluting the code

2

I need that when one animation finishes in jQuery, another one will start. And this must be done in sequence (without running at the same time).

I saw in a project where I work a code that does this, but I believe it has many callbacks.

var speed = 1000;
var cb = undefined;
$(function() {
  $('.item:eq(0)').animate({
    opacity: 1
  }, speed, function() {
    $('.item:eq(1)').animate({
      opacity: 1
    }, speed, function() {
      $('.item:eq(2)').animate({
        opacity: 1
      }, speed, function() {
        $('.item:eq(0)').animate({
          opacity: 0
        }, speed, function() {
          $('.item:eq(1)').animate({
            opacity: 0
          }, speed, function() {
            $('.item:eq(2)').animate({
              opacity: 0
            }, speed, function() {
              $.isFunction(cb) && cb();
            })
          })
        })
      })
    })
  })
});
.item {
  height: 30px;
  background: red;
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script><ul><liclass="item">item</li>
  <li class="item">item</li>
  <li class="item">item</li>
</ul>

In this case, if we had more than 3 items (suppose we had 20), I would not like to have 20 callbacks.

The questions are:

  • How could this code improve?
  • How can I make these sequential animations in jQuery (one waiting for another)?
asked by anonymous 13.08.2015 / 19:12

3 answers

2

You're looking for promise ;).

And your problem has a well-known name, it's called Callback Hell

    
13.08.2015 / 19:31
1

In this specific case you can do this without jQuery until. If I understand correctly you want to run the elements, show them, and then run again hiding. So successively. So it would look something like this:

CSS:

.item {
    opacity: 0;
    transition: opacity .8s;
}
.mostrar {
    opacity: 1 !important;
}

JavaScript

(function () {
    var speed = 1000;
    var items = document.querySelectorAll('ul li');
    var index = 0;

    function iterar() {
        setTimeout(iterar, speed);
        items[index].classList.toggle('mostrar');
        index++;
        if (index == items.length) index = 0;
    }
    iterar();
})();

jsFiddle: link

Using classList.toggle which is native you can add / remove a class in an element. So I created a iterar function that is calling itself and traversing the elements to give it or remove it.

    
13.08.2015 / 19:37
1

Here is an example of how to resolve:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><styletype="text/css">
        .item {
            height: 30px;
            background: red;
            opacity: 0;
          }
    </style> 
</head>
<body>    
   
    <ul>
      <li class="item">item</li>
      <li class="item">item</li>
      <li class="item">item</li>
    </ul>
</body>
<script type="text/javascript">
    
    function abrir(i){
        if(i == 3){ // QUANTOS ELEMENTOS IRÁ CRIAR ?
            fechar(0);
        }
        else{
            console.log(i);            
            $('.item:eq('+i+')').animate({ opacity: 1 }, 1000, function() { 
                i = i + 1;
                abrir(i); 
            });            
        }        
    };
    
    function fechar(i){
        if(i == 3){ // QUANTOS ELEMENTOS IRÁ CRIAR ?
            return 0; 
        }
        else{
            console.log(i);            
            $('.item:eq('+i+')').animate({ opacity: 0 }, 1000, function() { 
                i = i + 1;
                fechar(i); 
            });            
        }
    };
    
    $(function() {
        abrir(0);                
    });
</script>  
</html>
    
13.08.2015 / 19:49