solid90
solid90

Reputation: 127

Javascript List Item Animations

I am working on a very basic animation where classes are removed from list items once they have been loaded and appended to the document. The issue I am having is with the animation itself. I want the animation to execute in a stepped manner like the image below...

Animation I want

What actually though is that the loop runs completely, console.log messages get output in a stepped manner, but the classes are all removed at the same time once the loop has completed. How can I change this behavior? Why would the console.log messages be stepped but the classList.remove functionality is not executed at the same time?

Here is my code...

function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }
}


/**/
function showListItems() {
  var listItems = document.querySelector('.idList');
  var n = 20;
  var c = 0;
  var itemArray = new Array();
  for (var i = 0; i < listItems.children.length; i++) {
    var item = listItems.children[i];
    if (item.classList && item.classList.contains('idList__item--hide')) {
      console.log('Item: ', item);
      itemArray[c] = item;
      c++;
    }
  }
  console.log('Item Array: ', itemArray);
  itemArray.forEach(function(el, index) {
    sleep(n);
    el.classList.remove('idList__item--hide');
    console.log("EL[" + index + "]: ", el);
  });
}

I realize this code may look over complex and perhaps it is, but I have tried about everything I can think of. I have tried using promises, for loops, now the forEach method.

Thank you.

Upvotes: 0

Views: 1040

Answers (3)

Rocky Sims
Rocky Sims

Reputation: 3618

The browser doesn't update until javascript finishes running. Your script doesn't relinquished control back to the browser while sleeping so the browser can't update. This is exactly what setTimeout is for.

Change

itemArray.forEach(function(el, index) {
    sleep(n);
    el.classList.remove('idList__item--hide');
    console.log("EL[" + index + "]: ", el);
});

to

itemArray.forEach(function(el, index) {
    const ms = n * (index + 1);
    setTimeout(function() {
        el.classList.remove('idList__item--hide');
        console.log("EL[" + index + "]: ", el);
    }, ms);
});

We're scheduling all the remove calls in advance which is why we're multiplying n by index + 1.

And in case you're interested, here is the code I used to test sleep vs setTimeout. https://codepen.io/rockysims/pen/jeJggZ

Upvotes: 1

Kai
Kai

Reputation: 11

This may be a bad way but it should solve your problem.

You can use setTimeout() in forEach, and use index to change time parameter, like it:

itemArray.forEach(function(el, index) {
    setTimeout(function(){
         el.classList.remove('idList__item--hide')
    },500*(index+1)) 
});

Upvotes: 1

Umesh Maharshi
Umesh Maharshi

Reputation: 1591

I used Jquery each and setTimeout functions for chaining the animations.

$( "li" ).each(function( index ) {
    var listItem = $( this );
    setTimeout(function() {
      listItem.animate({
      opacity: 1
    }, 500); 
    }, (index + 1) * 500);
    
});
ul {
  padding : 20px;
}
ul li {
  list-style : none;
  height : 50px;
  background-color : lightgreen;
  margin : 20px;
  opacity : 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul>
<li>Hello world 1</li>
<li>Hello world 2</li>
<li>Hello world 3</li>
<li>Hello world 4</li>
</ul>

Upvotes: 0

Related Questions