Search code examples
javascriptcsscss-transitionscss-animationsweb-component

What is the best way to detect if an element has a CSS animation applied


I am attempting to fire an event on a web component when a CSS animation has completed, however there is a possibility the user might clear the animation from the element using animation: none; meaning the transitionend event never fires:

// wait for dialog close animation to end before firing closed event
element.addEventListener('transitionend', function() {

    // fire dialog closed event
    self.dispatchEvent(new CustomEvent('pure-dialog-closed', { 
        bubbles: true, 
        cancelable: true 
    }));
});

To ensure my custom event always fires, I need to determine if the element or any of its children have an animation applied and if not, fire the pure-dialog-closed event immediately.

I have tried checking style.animationName and self.style.transition but it does not appear to be working. I need a simple way of checking if an element, or any of its children have a CSS animation applied.


Solution

  • Thank you @Thusitha. I used window.getComputedStyle along with animation-duration and transition-duration to determine if an animation existed as either would need to be greater than 0s for an animation/transition to play out.

    The following inspects all elements including element passed in:

    /**
     * Determine if an element of any or its children have a CSS animation applied
     * @param {HTMLElement} el - element to inspect
     * @returns {boolean} true if an animation/transition detected, otherwise false
     */
    function hasCssAnimation(el) {
    
      // get a collection of all children including self
      var items = [el].concat(Array.prototype.slice.call(el.getElementsByTagName("*")));
    
      // go through each item in reverse (faster)
      for (var i = items.length; i--;) {
    
        // get the applied styles
        var style = window.getComputedStyle(items[i], null);
    
        // read the animation/transition duration - defaults to 0
        var animDuration = parseFloat(style.getPropertyValue('animation-duration') || '0');
        var transDuration = parseFloat(style.getPropertyValue('transition-duration') || '0');
    
        // if we have any duration greater than 0, an animation exists
        if (animDuration > 0 || transDuration > 0) {
          return true;
        }
      }
    
      return false;
    }
    
    var elementToTest = document.querySelector('.one');
    var result = hasCssAnimation(elementToTest);
    
    alert(result);
    div {
      font-size: 14px;
      padding: 20px;
      color: #fff;
    }
    
    .one {
      background: red;
    }
    
    .two {
      background: green;
      animation: zoomIn 3s ease; /* <-- animation applied to child */
    }
    
    .three {
      background: blue;
    }
    
    @keyframes zoomIn {
      from {
        opacity: 0;
        transform: scale3d(.3, .3, .3);
      }
      50% {
        opacity: 1;
      }
    }
    <div class="one">
      <div class="two"> <!-- animation on child element -->
        <div class="three">
          Hello World
        </div>
      </div>
    </div>