1

If an element has more than one of its css properties changed and they have differing transition durations, is there a way to detect the completion of the last/longest running transition.

Example:

<style>
.box {
    width: 100px;
    height: 100px;
    transition: width 0.5s, height 6s;
}

.animate {
    width: 400px;
    height: 400px;
}
</style>

<div class="box"></div>

<script>

  // I want to run some code after both the width and height transitions
  // are complete

  // Listening for transitionend events will fire on every transition 
  // that ends. So in this case, both width and height. Assume I don't 
  // know how many properties are being transitioned.
  $('.box').on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', function(ev) {
       // ...
  });
  $('.box').addClass('animate');

</script>
Lokesh Dhakar
  • 5,289
  • 5
  • 22
  • 24
  • Create a list by adding transitions when listening for `transitionstart`, then remove them when `transitionend` fires... – Heretic Monkey Jan 13 '17 at 22:04
  • can be considered as ugly but for what i needed (not picky about reaction time) it was perfect because i could generalize it: a JS plugin inteval function, relatively slow (250ms), that can be called to watch one or several CSS properties to see if they are still changing (store values) and throw multiple callbacks – Kaddath Jan 13 '17 at 22:16
  • @MikeMcCaughan looks like transitionstart is not in a spec yet and x-browser support is not so hot. I like the pattern you presented though, only if I could do that consistently x-browser. – Lokesh Dhakar Jan 13 '17 at 22:16

2 Answers2

0

You could find out the number of transitions and then count them down.

var box = document.getElementsByClassName('box')[0];
numTransitions = getComputedStyle(box).transition.split(',').length;

Kind of fragile if your CSS isn't clean, but maybe you have control over that

Stephen Thomas
  • 13,843
  • 2
  • 32
  • 53
  • Your example would not work in the case where some of the transitionable properties are not changed. For example, transition: color 1s, width 2s, height 3s. Then a class is added that changed just width and height. Your solution is getting closer though. – Lokesh Dhakar Jan 13 '17 at 22:31
  • You could look up the the properties being transitioned by looking up the definition of the class (assuming you're using a class) via the CSSOM. – Heretic Monkey Jan 13 '17 at 22:33
  • i guess the question is how badly do you need to know exactly when the transition ends. As opposed to, say, just setting a 1s timeout? – Stephen Thomas Jan 13 '17 at 22:36
  • @StephenThomas In the end I might opt for specifying the duration in JS and using a timeout. But I was curious if there was a relatively light and bullet-proof way to handle this. Sounds like it might not be the case. – Lokesh Dhakar Jan 13 '17 at 22:39
  • @MikeMcCaughan I was not aware that you could lookup the class definition! neato. It is prob overkill for my usecase, but it is good to know that this is feasible. Many examples of this lookup technique on SO: http://stackoverflow.com/questions/16965515/how-to-get-a-style-attribute-from-a-css-class-by-javascript-jquery – Lokesh Dhakar Jan 13 '17 at 22:41
-1

Yes it is possible but a bit tricky. You extract duration (and delay) from the transition properties, and find the one with the highest value. Since transitionEnd has the (transition)propertyName value, now you only have to compare this with the extracted property. Example is here. (be aware that you have to wait for 6 seconds until something happens)

function getMaxTransitionDuration(el) {
    // small helper to extract the values
    function extract(str) {
        return str
          .replace(/[A-Z]/gi, "")
          .split(", ")
          .map(parseFloat);
    };
  // get the current style
  var style = getComputedStyle(el);
  // get all transition properties
  var props = style.transitionProperty.split(", ");
  // we need delay and duration
  var delays = extract(style.transitionDelay);
  var durations = extract(style.transitionDuration);

  // combinate delay and duration
  var totals = durations.map(function(v, i) {
    return v + delays[i];
  });

  // find the property with longest value
  var max = totals[0]; 
  var maxIndex = 0;
  for (var i = 1; i < totals.length; i++) {
    if (totals[i] > max) {
      maxIndex = i;
      max = totals[i];
    }
  }
  // and return this property
  return props[maxIndex];
}

$('.box').on('transitionend', function(ev) {
    var lastProp = getMaxTransitionDuration(this);
    if (ev.originalEvent.propertyName == lastProp) { 
      // here we are.....
    }
});

$('.box').addClass('animate');
axel.michel
  • 5,764
  • 1
  • 15
  • 25
  • Bonus points for accounting for transition delay! Testing the code now. – Lokesh Dhakar Jan 13 '17 at 22:45
  • Your example would not work in the case where some of the transitionable properties are not changed. For example, transition: width 1s, height 2s, color 3s. Then a class is added that changes just width and height. We discussed in the comments on the other answer the possibility of looking up which properties a class is changing. This is turning out to be quite complex! Really appreciate the detail in your answer. If you want to add a note about the use case I noted above where it will not work I can accept it. Or if you want to take a stab at solving that part, go for it! – Lokesh Dhakar Jan 13 '17 at 22:57
  • @LokeshDhakar I got the point, I played around with the code, came up with this: https://jsfiddle.net/mzrc3tm8/4/. This seems to work as long as you add a class but will fail if you remove one, and its getting larger, A simpler approach could be using some data-attributes containing the info which properties will be manipulated. Not totally generic but maybe more handy than scanning a complex stylesheet which is possible, but expensive, and should be done outside of this event handler. – axel.michel Jan 14 '17 at 00:35