Only the elements that are every other one in the sequence will be iter

I am working on animating a list of elements with CSS3 animations. Here is an example of the code:

.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 */

My goal is to only animate visible elements with the 'animate' class when the page loads using JavaScript:

$(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');
            } 
        });
    }
});

However, I encountered a problem where only every second element is checked as visible. It should be checking each element individually like this:

https://i.sstatic.net/XdOGU.jpg

I want all elements to be animated correctly when they become visible while scrolling down the page, so I added this jQuery scroll function:

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

How can I make sure that every element is properly iterated through in this scenario?

EDIT:

After considering feedback from @T.J. Crowder, I updated the animate function by including a filter function suggested by @charlietfl:

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

This modification has resolved the issue. Thank you for your help.

Answer №1

There are a few issues to address:

  1. The set being modified ($toAnimate) is also being iterated over, resulting in indexes becoming incorrect after removals.

  2. It's important to note that splice is not an official jQuery method and may be removed in the future as it is undocumented. jQuery objects are not true arrays but rather array-like.

  3. jQuery does not provide a clear guarantee on how each will behave when entries are added or removed from the set being iterated (unlike JavaScript's forEach).

To avoid these issues, you can convert $toAnimate into a legitimate array using .get:

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

Then, you can proceed with the following approach:

function animate()
{
    $toAnimate.forEach(function(el)
    {
        var $el = $(el);
        if (isElementVisible($el))
        {
            // Remove visible elements
            $toAnimate.splice(i, 1);

            // Set up animation effect
            if( $el.data('effect') == 'anim-bar' ) animateBar($el);
            else $el.addClass( $el.data('effect') );

            $el.removeClass('animate');
        } 
    });
}

Answer №2

By iteratively removing items from the array within the loop, you are causing the next item to shift into the current position, potentially resulting in skipping over an element. However, if you reverse the loop and iterate through the array from the end, removing items will not disrupt the remaining elements:

function executeAnimation()
{
    for (var index = $elementsToAnimate.length - 1; index >= 0; index--)
    {
        var $element = $elementsToAnimate.eq(index);

        if ($element.length && isVisible($element))
        {
            // Remove already visible elements
            $elementsToAnimate.splice(index, 1);

            // Apply animation effect
            if( $element.data('effect') == 'anim-bar' ) animateBar($element);
            else $element.addClass( $element.data('effect') );

            $element.removeClass('animate');
        } 
    });
}

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

Stylize the selected item in a kendoui listview

I am utilizing a kendoui listview to showcase various signatures in an ASP.NET MVC 5 project. My goal is for the listview to allow selection and exhibit data based on a predefined template. Everything is functioning properly, except for an unwanted margi ...

Launching a popup within a popup

I encountered an issue with opening multiple modals in Bootstrap. When I try to open a new modal from another modal, the background darkens but the content I need to show inside the new modal doesn't appear. It seems to be hidden. Has anyone faced a s ...

Apply a new class to all Radio buttons that were previously selected, as well as the currently

Struggling with a simple code here, trying to figure out how to add color (class) to the selected radio button and the previous ones. For example, if button No3 is selected, buttons 1, 2, and 3 should get a new color. Below is the basic code snippet: < ...

Guide to transforming Excel formulas into JavaScript

In my Excel sheet, I have the following formula: =IF($F6=0,"",IF(I6=0,"",$F6/I6)) where F6=7000 and I6 is empty. The Excel result is displaying no data for the formula. Now, I need to convert this formula into JavaScript. function AB6(F6) { var ...

Creating a button inside an input field using HTML and CSS

Is there a way to place a button inside a text input field? I want to achieve this design: https://i.sstatic.net/q2M9v.png This is my current code: input { background: #8196aa; border: 0; border-radius: 10px; color: whi ...

Sending information from a directive to a controller

I'm working on passing a resource from a directive scope to a controller scope by utilizing a callback provided in the view. However, I'm encountering an issue where the argument variable is showing as undefined. Can you help me figure out what I ...

Await that's locked within a solo asynchronous function

async function submitForm(e){ e.preventDefault() console.log(e.target) try { const response = await axios.post('/api/<PATH>', {username, password}); console.log(response.data); const token = response.data.token if (t ...

Adding vertical separation between Bootstrap Cards

Currently, I am utilizing bootstrap cards, and this is the current appearance of my setup: https://i.sstatic.net/LstAX.png My goal is to create more vertical space between the cards so that Card 1 and Card 3 are not directly next to each other, as well as ...

Tips for aligning multiple columns within a Bootstrap 3 carousel

For quite some time, I have been trying to solve this puzzle. My goal is to have the featured image displayed to the left of the carousel, with its associated text to the right. I've created a mockup to illustrate exactly what I mean. I can achieve t ...

Struggling with creating an html file that is responsive on mobile devices

I am currently utilizing bootstrap 4 for the construction of a website. I have encountered an issue where my homepage does not display properly on handheld devices when using Google Chrome's device toggle toolbar. The homepage requires scrolling to vi ...

Reorganize the data in a different manner (collections of items instead of individual items linked to groups)

Is there a way to create a function that converts data from `DevicesType` to `GroupsType` below? Instead of having a list of devices showing the groups they belong to, I need a list of groups with their respective devices. type DevicesType = { id: string ...

Bring to life by pressing a Trigger button

One of my div elements expands in height when clicked. The jQuery code responsible for this behavior is shown below: $('.contact-click').click(function() { $('#rollout').animate({ height: "375", }, 500); }); ...

The binding in Knockoutjs is working properly, but for some reason the href attribute in the anchor tag is not redirecting to

Here is the HTML code snippet I am working with: <ul class="nav nav-tabs ilia-cat-nav" data-toggle="dropdown" data-bind="foreach : Items" style="margin-top:-30px"> <li role="presentation" data-bind="attr : {'data-id' : ID , 'da ...

Initiating actions set post DOM load - JIRA unveils fresh problem modal

I have developed a custom plugin for the popular Atlassian JIRA platform which includes a calendar feature. However, I encountered an issue where clicking on a day outside of any event triggers the `#create_link event`. When this event is triggered, a new ...

Tips on using the map and filter methods to narrow down a nested array based on specific object

I am struggling to filter JSON data based on a specific date using Array.Filter and Map. The code I have tried below is not yielding the desired results. Can someone please provide guidance on how to effectively filter JSON data based on a particular date ...

What is the best way to select both an inline element and a block-level element at the same time?

After receiving a shortcode from a WordPress theme, the output is: <div class="heading clearfix"><i class="icon icon-list-ol h2"></i> <h2> Hello World! </h2></div> I am looking to style both the icon and h2 elements to ...

Utilize a counter to iterate the name repeatedly

echo "<td>".'<input type="text" name="date_from" class="datepicker"/>'."</td>"; Is there a way to utilize a counter to repeat the name attribute and then store it using $_POST[]? ...

Organize an array based on its ratio

I am attempting to organize an array based on the win and lose ratio of each player. This is how my code currently looks: const array = [{playerName: 'toto', win: 2, lose: 2}, {playerName: 'titi', win: 0, lose: 0}, {playerName: &apo ...

Implementing dual backgrounds in CSS

I am trying to achieve a gradient background for li elements along with different image backgrounds for each individual li. Is there a way to accomplish this? List of items: <div id="right_navigation_container"> <ul> <li id ...

Have you ever wondered how the automatic video play on scroll feature works on Tiktok.com and YouTube shorts when using a mobile device?

My goal with React JS is to develop a website similar to Tiktok, where the video below will automatically play with sound as the user scrolls down. I attempted to set this up using window.addEventListener("scroll",...), but after looking into it further, ...