When Using CSS Transitions/Animations/Etc., What's the Best Way to Fall Back to Jquery If the Users Browser Doesn't Do CSS Animations

When using CSS transitions/animations/etc., what's the best way to fall back to jquery if the users browser doesn't do css animations?

The jQuery animate enhanced plugin uses CSS transitions without having to write specific code for transition capable browsers

The alternative is not very encouraging: you could add a feature detection library such as Modernizr and then write specific code for every case, such as...

if (Modernizr.csstransitions) {
$("#yourdiv").css({
"-webkit-transform" : "translate(0, 10)",
"-o-transform" : "translate(0, 10)",
"-moz-transform" : "translate(0, 10)",
"-ms-transform" : "translate(0, 10)",
"transform" : "translate(0, 10)"
});
}
else {
//do jquery animate stuff
}

css transition doesn't work if element start hidden

To understand plainly the situation, you need to understand the relation between the CSSOM and the DOM.

In a previous Q/A, I developed a bit on how the redraw process works.

Basically, there are three steps, DOM manipulation, reflow, and paint.

  • The first (DOM manipulation) is just modifying a js object, and is all synchronous.
  • The second (reflow, a.k.a layout) is the one we are interested in, and a bit more complex, since only some DOM methods and the paint operation need it. It consists in updating all the CSS rules and recalculating all the computed styles of every elements on the page.

    Being a quite complex operation, browsers will try to do it as rarely as possible.
  • The third (paint) is only done 60 times per seconds at max (only when needed).

CSS transitions work by transitioning from a state to an other one. And to do so, they look at the last computed value of your element to create the initial state.

Since browsers do recalculate the computed styles only when required, at the time your transition begins, none of the DOM manipulations you applied are effective yet.

So in your first scenario, when the transition's initial state is calculated we have

.b { computedStyle: {display: none} }

... and that's it.

Because, yes, that's how powerful display: none is for the CSSOM; if an element has display: none, then it doesn't need to be painted, it doesn't exist.

So I'm not even sure the transition algorithm will kick in, but even if it did, the initial state would have been invalid for any transitionable value, since all computed values are just null.

Your .a element being visible since the beginning doesn't have this issue and can be transitioned.

And if you are able to make it work with a delay (induced by $.animate), it's because between the DOM manip' that did change the display property and the execution of this delayed DOM manip' that does trigger the transition, the browser did trigger a reflow (e.g because the screen v-sync kicked in between and that the paint operation fired).


Now, it is not part of the question, but since we do understand better what happens, we can also control it better.

Indeed, some DOM methods do need to have up-to-date computed values. For instance Element.getBoundingClientRect, or element.offsetHeight or getComputedStyle(element).height etc. All these need the entire page to have updated computed values so that the boxing are made correctly (for instance an element could have a margin pushing it more or less, etc.).

This means that we don't have to be in the unknown of when the browser will trigger this reflow, we can force it to do it when we want.

But remember, all the elements on the page needs to be updated, this is not a small operation, and if browsers are lenient to do it, there is a good reason.

So better use it sporadically, at most once per frame.

Luckily, the Web APIs have given us the ability to hook some js code just before this paint operation occurs: requestAnimationFrame.

So the best is to force our reflow only once in this pre-paint callback, and to call everything that needs the updated values from this callback.

$('button').on('click',function(){  $('.b').show(); // apply display:block synchronously    requestAnimationFrame(() => { // wait just before the next paint    document.body.offsetHeight; // force a reflow    // trigger the transitions    $('.b').css('right','80%');    $('.a').css('right','80%');  });})
body {  width:800px;  height:800px;}
div { width:50px; height:50px; background-color:#333; position:absolute; display:none; right:5%; top:0; transition:right .5s cubic-bezier(0.645, 0.045, 0.355, 1); color: white;}
.a { display:block; top:60px;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><div class='a'>A</div><div class='b'>B</div><button>Launch</button>

CSS3 animated spinner workaround for IE

Try the javascript plugin, spin.js

On its site it says it Works in all major browsers, including IE6 so it should work with IE8.

It's really simple to use, I use it all the time.

Transitions on the CSS display property

You can concatenate two transitions or more, and visibility is what comes handy this time.

div {  border: 1px solid #eee;}div > ul {  visibility: hidden;  opacity: 0;  transition: visibility 0s, opacity 0.5s linear;}div:hover > ul {  visibility: visible;  opacity: 1;}
<div>  <ul>    <li>Item 1</li>    <li>Item 2</li>    <li>Item 3</li>  </ul></div>

Bypassing transition and changing a property instantly

Since nobody else is posting a valid answer, here goes:

$('div').css('width', '200px').delay(1000).queue(function() {
$(this).css({transition: '0s', width: '10px'}).delay(1).queue(function() {
$(this).css({transition:'2s'});
});
},1000)​;

FIDDLE

Or if it's the other way:

$('div').css({
transition: '0s'
}).css('width', '200px').delay(1000).queue(function() {
$(this).css({width: '10px', transition: '2s'});
});

FIDDLE

jQuery should normalize vendor prefixes these days, so you don't have to type them all yourself.


The issue here is that jQuery attaches all the styles at once, only keeping the last styles, overwriting the previous styles of the same CSS property without ever doing a repaint of the DOM, and testing with native javascript seems to be doing the same thing, so it's probably the browser trying to avoid uneccessary reflows by adding a style just to have it changed in the next line of code, so doing:

$('div').css({
transition: '0s',
width: 200
}).css({
transition: '3s',
width: 10
});

won't work as only the last style is added.

This is where delay() comes into play, the OP's question was already using delay() so there was no reason not to use it, but removing delay() will of course cause the above issue, where the browser doesn't paint the first style, but only the last etc.

As delay() is really just a fancy timeout, it effectively defers the execution of the second setting of the styles, causing two browser repaints.

As this is most likely a browser issue, and not something we can change, deferring the setting of the second style is the only way to make this work, and using a delay will still work even if it's set to just 1 milliseconds, or one could defer the execution with a regular timeout, which is the usual way to defer execution of a script:

$('div').css({
transition: '0s',
width: 200
});

setTimeout(function() {
$('div').css({
transition: '3s',
width: 10
});
});

FIDDLE

The above will work just fine, as the timeout causes the first setting of the style to be painted by the browser, and defers the setting of the style inside the timeout to a later time, but as no time is set, it's executed as soon as the browser can (but still deferred until after the current script has completed), which for the human eye would seem like immediately, and that solves the issue.

CSS animation won't trigger on addClass in jQuery

You need to add the class to the element after it is rendered to the dom, a set timout might work

setTimeout(function(){
$articleHTML.addClass("animate");
}, i * 500 );

http://jsfiddle.net/Pz5CD/1/



Related Topics



Leave a reply



Submit