Search code examples
jquerycss-animationsevent-bubblingfunction-call

Why Does Adding Keyframe Animation To Element Via AddClass Trigger Entire Function In Javascript?


Problem:

When animation is added via class (elm.addClass) instead of simply changing the classes of each element, jQuery/JS runs entire function for each element with change of class.

Have attempted numerous scenarios to understand why this behavior is occurring (i.e. vanilla JS, jQuery, multiple functions, etc. etc.), result is can easily get slider to work (actually commented out code that works) but Why Is The Code not behaving as prescribed in docs? It should not be triggering the function for each class change - almost a bubbling type effect (or could it somehow be possible? If so, why?)

Simple Solution:

Of course can avoid this behavior by controlling animation via JS instead of CSS (elm.fadeOut), but that does not explain why JS is behaving as though it has a bug.

Summary:

Why is adding animation via adding class triggering function (or bubbling effect) for each element that gets changed? Each element should animate without re-triggering function.

let imgnx = 0
function fadeImg() {
  if (imgnx < imgSrc.length) {
    $('#ctnr').append(`<img class='slides show' src=${imgSrc[imgnx]} alt='slide ${imgnx +1}'>`)
    $(`.show:eq(${imgnx})`).on('animationend', function() {
      imgnx = imgnx + 1
      fadeImg()
    })
  } else {
    console.log(`All slides displayed', 'image index is: ${imgnx}`);
    $(".slides").removeClass("show").addClass("hide");
    $(".hide").last().on('animationend', function () {
      // $(".slides").remove(); imgnx = 0; fadeImg();
      $('#ctnr').fadeOut('slow').fadeIn('slow')
    })
    
    console.log('Class removed - class show removed, index is: ' + imgnx)
  }
}

Test Complete Code at: https://playcode.io/351331?tabs=script.js,preview,console


Solution

  • Why Does Adding Keyframe Animation To Element Via AddClass Trigger Entire Function >In Javascript?

    The answer to this question is determined by the code structure.

    Firstly, just in case anyone is unsure, adding CSS Keyframe Animation to an api or block of code does not inherently re-trigger a JavaScript function. Though it can be designed to do this [in conjunction with additional code] if so desired, it's organic design is not intended for such purposes. In this particular case, where adding CSS Animation via class addition appears to invoke its own function, the animation itself is not calling the function [or simulating a function call] but rather this unexpected behavior is a result of incomplete code.

    Code Error

    When observing the code in action we can easily see the unintended behavior that results in various code blocks firing when not desired to do so. Upon further inspection examiners will note the particular code block related to this behavior. If we zero in on this code block, we will find that the EventListener has been turned on but never turned off (in pure JS terms it has been added but never removed). This is where the problem lies. Because the EventListener has never been turned off it is still listening for any animations to end. When additional animations are added to the element being listened to (via class or any other method) the EventListener fires just as it should and the handler kicks in, implementing whatever code it is given to run. This results in the strange behavior which appears to recall the function.

    Solution

    Once the problem code is located the solution is obvious. Simply turn off the EventListener at the appropriate location (multiple options) and the app runs flawlessly.

    Suggested Sample Code

    else { /* Try code below */
    // $('.show').off('animationend')
    console.log(`All slides displayed', 'image index is: ${imgnx}`);
    $(".slides").removeClass("show").addClass("hide");
    /* Try code here */ 
    // $('.show').off('animationend')
    $(".hide").last().on('animationend', function () {
      $(".slides").remove(); imgnx = 0; fadeImg();
      $('#ctnr').fadeOut('slow').fadeIn('slow')
    })
    

    Conclusion

    Yes, there are various workarounds etc., but it is usually much better to maintain proper programming standards and implement complete and proper code versus working around incomplete or improper code. Additionally, the code suggestions included are mere samples and by no means the extent of possible solutions. A little more code interplay and boundless solutions are sure to come.

    But more importantly, this exercise serves to demonstrate the value of removing or turning off EventListeners.

    Thank you.