JSFiddle - React, Tailwind, and code Playground

HTML

<div>
    <p />
</div>

CSS

p{
    opacity: 0;
    transition: opacity 10s, transform 5s;
    background: red;
    width: 50px;
    height: 50px;
    margin: 100px;    
}

div.visible p{
    opacity: 1;
    transform: scale(1.5);
}

JavaScript

(function($){
    
  $.event.special.transitionsComplete = {

    setup: function(data, namespaces, eventHandle){    
      var queue = [],
          style = window.getComputedStyle(this, null),
          computedProps = style.getPropertyValue('transition-property').split(', '),
          computedDurations = style.getPropertyValue('transition-duration').split(', '),
          $node = $(this);          

      //console.log(computedProps, computedDurations, this);
        
      // only count properties with duration higher than 0s
      for(var i = 0; i < computedDurations.length; i++)
        if(computedDurations[i] !== '0s')
          queue.push(computedProps[i]);
        
      console.log('remaining', queue);        
               
      // there are transitions
      if(queue.length > 0){
        $node.on('webkitTransitionEnd.x transitionend.x', function(e){          
          queue.splice(queue.indexOf(e.originalEvent.propertyName));
          console.log('remaining', queue);            
          if(queue.length < 1)
            $node.trigger('transitionsComplete');
        });

      // no transitions, fire (almost) immediately
      }else{
        setTimeout(function(){
          $node.trigger('transitionsComplete');
        }, 5);

      }

    },

    teardown: function(namespaces){
      $(this).off('.x');
    }

  };
})(jQuery);


var div = $('div'), p = $('p'), start = new Date().getTime();
console.log('-- start --');
div.addClass('visible');

div.one('transitionsComplete', function(e){
    console.log('complete-div', (new Date().getTime() - start) / 1000);
});

p.one('transitionsComplete', function(e){
    console.log('complete-p', (new Date().getTime() - start) / 1000);
});