Only every second element is repeated

I have a list of elements for animating through CSS3, for example:

.anim-slide-left { animation: anim-slide-left 0.8s ease forwards; -webkit-animation: anim-slide-left 0.8s ease forwards; } @-webkit-keyframes anim-slide-left { 0% { transform: translateX(-500px); -webkit-transform: translateX(-500px); opacity: 0; } 100% { transform: translateX(0); -webkit-transform: translateX(0); opacity: 1; } } /* there are more, but very similar */ 

When the page loads, js should animate only visible elements with the special class 'animate':

 $(function() { var $window = $(window); var $toAnimate = $('.animate'); animate(); // check if element is on the viewport function isElementVisible(elementToBeChecked) { var TopView = $(window).scrollTop(); var BotView = TopView + $(window).height(); var TopElement = elementToBeChecked.offset().top; return ((TopElement <= BotView) && (TopElement >= TopView)); } // add css animation class function animate() { $toAnimate.each(function(i, el) { var $el = $toAnimate.eq(i); if ($el.length && isElementVisible($el)) { // remove already visible elements $toAnimate.splice(i, 1); // setting up animation effect $el.addClass( $el.data('effect') ); $el.removeClass('animate'); } }); } }); 

Now here is the problem. Only every second element is checked as visible, for example:

enter image description here

But it should be like this:

enter image description here

The remaining elements are animated only when the page scrolls down:

 $window.scroll( function() { animate(); }); 

How to iterate over each item in this scenario?

EDIT:

Considering @TJ Crowder comments, I modified the animation function with the filter function suggested by @charlietfl:

 $('.animate').filter( function( idx ) { if( isElementVisible($(this)) ) { $(this).addClass( $(this).data('effect') ); $(this).removeClass('animate'); } }); 

It works just fine :) Thanks guys.

+4
source share
2 answers

There are several problems:

  • You modify the set ( $toAnimate ) that you are executing, and you retrieve elements from that set with an ever-increasing index. Therefore, naturally, if you delete one, from that moment your indexes will be disabled.

  • splice not an official jQuery method. It is undocumented and may disappear at any time. (jQuery objects are not arrays, they just look like arrays.)

  • As far as I know, jQuery makes no guarantees what each will do if you add or remove entries in the set that you are repeating (unlike JavaScript forEach ).

Since you have splice and an iteration guarantee from forEach , you can make $toAnimate actual array using .get :

 var $toAnimate = $('.animate').get(); // ---------------------------^^^^^^ 

... and then:

 function animate() { $toAnimate.forEach(function(el) { var $el = $(el); if (isElementVisible($el)) { // remove already visible elements $toAnimate.splice(i, 1); // setting up animation effect if( $el.data('effect') == 'anim-bar' ) animateBar($el); else $el.addClass( $el.data('effect') ); $el.removeClass('animate'); } }); } 
+5
source

You remove the elements from the array you iterate, so the next element will replace the current one. When you move to the next item, it will skip one item.

If you end the array from the end, deleting the elements does not affect the elements later in the loop:

 function animate() { for (var i = $toAnimate.length - 1; i >= 0; i--) { var $el = $toAnimate.eq(i); if ($el.length && isElementVisible($el)) { // remove already visible elements $toAnimate.splice(i, 1); // setting up animation effect if( $el.data('effect') == 'anim-bar' ) animateBar($el); else $el.addClass( $el.data('effect') ); $el.removeClass('animate'); } }); } 
+1
source

All Articles