5

I want to skip a CSS transition under certain conditions. I'd prefer not to add special no-transition styles to my stylesheet, or duplicate my stylesheet's styles in my JavaScript. So far, the best solution I've found is

if (condition) {
  $el.css({'-webkit-transition-duration': '0s'});
  setTimeout(function() {
    $el.css({'-webkit-transition-duration': ''});
  }, 0);
};
$el.addClass('transitionClass');

(I've omitted the non-WebKit CSS for brevity. See it in action at http://jsfiddle.net/TrevorBurnham/zZBhx/.)

I don't like this because

  1. It's verbose, and
  2. It introduces potential race conditions, e.g. if another timeout is on the queue that will add or remove a class on $el.

Is there a better way?

4
  • jqueryui.com/demos/animate have a look at this, :) Commented Mar 27, 2012 at 14:30
  • @Val Doesn't seem relevant. I know I could use JavaScript-calculated animations instead, but I'm trying to work with CSS transitions here. Commented Mar 27, 2012 at 15:56
  • you are using javascript any way on your example above I dont see why you go the extra mile for a simple solution :) what could possibly be bad about that it's cross browser solution. Commented Mar 28, 2012 at 9:13
  • @TrevorBurnham checkout my new answer dude, I believe it is a better method - and no dependence on jQuery, just regular ol' JS. If you clone the element, you lose events that have been set on it and a whole host of other potential weirdness could occur, the solution I present below avoids all that :) Commented Jun 1, 2013 at 5:57

4 Answers 4

4

I know you said you didn't want to duplicate the style of the whole object in CSS, but have you thought about adding a special class just for the transition? Then you can define the element like this in the HTML:

<div id='#yourobject' class='transition'>

And when you don't want the transition, just do this (assuming jQuery):

$('#yourobject').removeClass('transition');
.... do some manipulation here
$('#yourobject').addClass('transition');
Sign up to request clarification or add additional context in comments.

2 Comments

Was also going to recommend this. Not sure why you need to go through the trouble of making a large function of sorts or cloning objects. Just apply the class when you need it. If you don't, remove it for the time being and apply it when you need it again.
I used this approach, but I had to do the addClass() in a setTimeout() with a time of 0. I'm assuming that the css change gets added to the event thread, so you have to make sure you add the transition after that happens?
3

Here is a reliable method for skipping an element's CSS transition, the code comes from Mozilla's X-Tag Web Components library:

var prefix = (function () {
  var styles = window.getComputedStyle(document.documentElement, ''),
      pre = (Array.prototype.slice
        .call(styles)
        .join('')
        .match(/-(moz|webkit|ms)-/) || (styles.OLink === '' && ['', 'o'])
      )[1];
  return {
    dom: pre == 'ms' ? pre.toUpperCase() : pre,
    lowercase: pre,
    css: '-' + pre + '-',
    js: pre[0].toUpperCase() + pre.substr(1)
  };

})();

var requestFrame = (function(){
  var raf = window.requestAnimationFrame ||
    window[prefix.lowercase + 'RequestAnimationFrame'] ||
    function(fn){ return window.setTimeout(fn, 20); };
  return function(fn){
    return raf.call(window, fn);
  };
})();

var skipTransition = function(element, fn, bind){
  var prop = prefix.js + 'TransitionProperty';
  element.style[prop] = element.style.transitionProperty = 'none';
  var callback;
  if (fn) callback = fn.call(bind);
  requestFrame(function(){
    requestFrame(function(){
      element.style[prop] = element.style.transitionProperty = '';
      if (callback) requestFrame(callback);
    });
  });
};

HOW TO USE IT - this snippet assumes you have set a transition on the foo element's width and want to change it to 100px immediately without the element transitioning.

var foo = document.querySelector('#foo')
skipTransition(foo, function(){
    foo.style.width = '100px';
});

LIVE EXAMPLE

Click each of the colored divs in the example - the red div has its width style set within the skipTransition function, thus preventing the transition and setting the style immediately. The blue div has its width set normally without skipTransition and the CSS transition effect occurs as normal: http://codepen.io/csuwldcat/pen/nFlse

4 Comments

This seems like the same approach described in the question; only the details are different (support for multiple browser prefixes, RequestAnimationFrame instead of a timeout). But it's still essentially the same process: 1) Disable transitions, 2) Apply styles, 3) Re-enable transitions after a timeout.
The overall approach is very similar, with two huge differences: using requestAnimationFrame is far better than a setTimeout, the latter can fail intermittently in normal usage (when frames drop, or JS calcs run long, etc), secondly you need to drop the transition-property style not the duration - Chrome will not respect nullified durations and still fire off the transition in some cases (like attempts to skip transition during page load, etc)
@TrevorBurnham having the user's fn called inside the first RAF, separate from the temporary transition-property removal, is unnecessary thus I moved it out - there is no longer any chance of a race condition. Can you remove your comment, as it is no longer valid?
@Nonconformist you're not giving a lot to go on - I've retested the demo and it works in all browsers that support CSS transitions. Are you sure it isn't something in your code? Can you paste your code in a demo on Codepen? --> codepen.io
2

Here's one approach: Clone the element, add the class to the clone, and replace the original element with the clone. Bam! No transition.

Demonstration: http://jsfiddle.net/TrevorBurnham/yXhBz/

This isn't ideal, though, because this breaks any stored references to the original element that you may have in your application. So I'd welcome an answer that operates synchronously on the existing element.

1 Comment

Accepting this as an answer, but better solutions are still very welcome.
1

Here are a couple of jquery plugins I wrote:

$.fn.redraw = function(){
    var redraw = $(this).first()[0].offsetHeight || $('body')[0].offsetHeight; // forces a draw in the main window if the element is off screen
    return this;
};

$.fn.cssNoTransition = function(){
    $(this)
            .css('transition', 'none')
            .css.apply(this,arguments)
            .redraw()
            .css('transition', '');
    return this;
};

Assuming the css says to transition height, use it like this:

this.$el.cssNoTransition({height: 0}).css({height: 200});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.