Can't set up CSS3 transition to allow one-second highlight - css

I'm trying to ditch setTimeout-based JavaScript in favor of a CSS transition for a one-second highlight. Instead of changing the background color instantly or even fading between two background colors, I'd like to first switch to yellow immediately, then fade to the proper color.
The way I imagine it should work is I set the class to something with a yellow background, then change it to a class with a transition property and a new color. This just fades to the final color instead of starting from yellow. I don't think I can attach the transition to the element irrespective of class or the two class changes will overlap and run their animations at the same time.
Clearly I need to be structuring things in a different way to accomplish this. I must not be grasping the basics of when transitions actually occur when attached to classes that are added and removed.
<style>
.highlight { background-color: yellow; }
.selected { background-color: black; -moz-transition: background-color 2s linear; }
</style>
<div id="samp" onclick="flash();">SAMPLE</div>
<script>
function flash() {
document.getElementById("samp").className = "highlight";
document.getElementById("samp").className = "selected";
}
</script>

After consulting IRC, it was determined the root cause of this was the browser waiting until a script block was complete before attempting a repaint/redraw. It was as if the highlight class was never applied. For this reason, Lea's solution above in fact does work if the timeout is increased. The exact timeout that works must depend on the repaint interval.
I searched for ways to force a repaint and came across an Opera blog post warning against measuring as it causes a repaint. Simply inserting var x = document.getElementById("samp").offsetWidth; between the two lines setting className causes it to start the transition from yellow as desired.
What I find interesting is that the optimization done by both Webkit and Gecko--waiting to redraw until a script block is complete--is reasonable when the actions taken are mostly stateless, as CSS is. That is, regardless of how many changes are made, a browser could discard all previous styles and be correct in applying the last. However, with transitions, CSS has become stateful, and this optimization actually produces very different results than if it were turned off.

You need to switch the class with a timeout:
function flash() {
document.getElementById("samp").className = "highlight";
setTimeout(function(){
document.getElementById("samp").className = "selected";
}, 10);
}
also, this is offtopic but a good practice is to avoid redundant DOM queries and cache elements in variables:
function flash() {
var samp = document.getElementById('samp');
samp.className = "highlight";
setTimeout(function(){
samp.className = "selected";
}, 10);
}

Related

Can I trigger CSS transitions via selectors other than ":hover" and ":active"?

I'm learning about transitions in CSS3 via sololearn.com, where the only examples given for using transitions are in conjunction with the :hover selector.
Everything else I can find uses either that or :active with no explanation of what other selectors can be used or why selectors other than these two are not shown as examples.
Is there a way to start transitions without interaction from the user? Or would that require JavaScript?
tl;dr
You can use any selector to initiate a transition, but you don't really need one for it. All you need is to:
set the transition properties on the element
change the value of the property to be transitioned.
You can do both of the above without using CSS selectors, which are nothing more than a handy method available for setting DOM element properties.
initial answer
transitions work with any selector whatsoever. But they are not related to any selector and you don't really need selectors to perform CSS transitions.
All you need for a transition to work is to set the following transition properties:
transition-duration,
transition-delay,
transition-timing-function
transition-property. (has to be animatable for transition to work).
After this, whenever the property named in transition-property changes its value between two animatable values, the change between the initial and the set value happens according to the set transition properties.
This happens regardless of what exactly triggers the change of value for the transitioned property
It could:
change because a particular CSS selector (you defined) starts or stops applying (matching the element);
be changed directly using JavaScript.
Or, of course, by a combination of the two (i.e. use JavaScript to apply a class, which changes the value of the property being transitioned)
So, in effect, the answer to your question is: transitions work with any selector but they also work without one. They work based on DOM element property values, regardless of any selector.
A common way to demonstrate a transition is using :hover state because all one needs to do to switch between selectors (which is to control / demonstrate / observe the transition) is. well,... hover!
On:
For example is there a way to start transitions without interaction from the user?
Of course there is! A very basic example is to add a class on <body> when all page resources have finished loading.
When <body> element is created it doesn't have the class and when all resources have finished loading, the Window's onload event triggers. We can use this event to add the class. If a transition is properly defined on body and the transitioned property changes because a stronger selector now applies on the element, the transition will happen. Without any user interaction.
See it working:
window.onload = function() {
document.querySelector('body').classList.add('loaded')
}
body {
background-color: #fff;
transition: background-color 1s linear;
}
.loaded { background-color: #212121;}
You could also set a recursive function that toggles the state of an element and calls itself after a set amount of time, effectively toggling the state back after the set time, in a continuous cycle:
For the above example this would mean:
window.onload = toggleBodyClass;
function toggleBodyClass() {
document.querySelector('body').classList.toggle('loaded');
setTimeout(toggleBodyClass, 1234);
}
body {
background-color: #fff;
transition: background-color 1s linear;
}
.loaded { background-color: #212121;}
If the question is: "Can a selector start/stop applying without the use of JavaScript and/or user interaction?" It really translates to:
"Are there transitions triggered by something else than what usually triggers a transition?", with possible answers varying from:
"Why do you want to know?" to "Is there web browsing without JavaScript and/or user interaction?"
Transitions are, as Paulie_D says, changes in state. What this "state" refers to, is simply the value of any style property (that can be animated, anyway). Even the spec describes it the same way:
Normally when the value of a CSS property changes, the rendered result is instantly updated, with the affected elements immediately changing from the old property value to the new property value. This section describes a way to specify transitions using new CSS properties. These properties are used to animate smoothly from the old state to the new state over time.
This means that you don't actually need a selector to start transitions. Styles can be changed via CSS, JavaScript, or the inline style attribute. All of these are subject to transitions, because they all modify style property values. Selectors just happen to be the most common way of doing it because selectors and declaration blocks are the two fundamental components that make up style rules, themselves the building blocks of CSS.
Most other ways of using transitions with or without user interaction do in fact involve JavaScript because CSS doesn't support much state change without requiring user interaction (outside of animations, which are a different beast from transitions), but that doesn't mean that JavaScript is inherently required for transitions to work, because transitions are about state change, regardless of how that state change is invoked (JS or not).
Most tutorials use :hover and :active to demonstrate transitions simply because user interaction is the easiest and most intuitive way for readers to see transitions in action, and to learn what it means for an element to change state (albeit a different kind of state, but the principle is the same). It's also by far the most common use case for transitions: animating changes in state in response to user interaction.
But you don't actually need to change property values with a user interaction pseudo-class in order for transitions to work. You can change them with any selector, even if user interaction is handled by a different element (and that element doesn't have to start transitions using :hover or :active either)...
label {
transition: color 1s linear;
}
:checked + label {
color: #f00;
}
<p><input type=checkbox id=check><label for=check>Label</label>
... or by the page itself...
h1 {
transition: color 1s linear;
}
:target {
color: #f00;
}
<p><a href=#sec1>Section 1</a> <a href=#sec2>Section 2</a>
<h1 id=sec1>Section 1</h1>
<h1 id=sec2>Section 2</h1>
Once you add JavaScript into the mix, you can set property values directly (i.e. not use a selector)...
setTimeout(function() {
document.querySelector('p').style.color = '#f00';
}, 1000);
p {
transition: color 1s linear;
}
<p>Wait for it...
... change an element's class, ID or other attribute...
setTimeout(function() {
document.querySelector('p.foo').className = 'bar';
}, 1000);
setTimeout(function() {
document.getElementById('foo').id = 'bar';
}, 2000);
setTimeout(function() {
document.querySelector('p[data-attr]').dataset.attr = 'bar';
}, 3000);
p {
transition: color 1s linear;
}
p.bar { color: #f00; }
p#bar { color: #090; }
p[data-attr=bar] { color: #00f; }
<p class=foo>Wait for it...
<p id=foo>Wait for it...
<p data-attr=foo>Wait for it...
... or even move elements around in the DOM tree (although this does have limitations — notice that the Foo item fails to start its transition because it's getting detached before being reattached, while the Bar item is able to start its transition once it notices it's now first because it never leaves the DOM tree)...
setTimeout(function() {
var ul = document.querySelector('ul');
ul.appendChild(ul.firstChild);
}, 1000);
li {
transition: color 1s linear;
}
li:first-child { color: #f00; }
<ul><li>Foo<li>Bar</ul>
... and be able to start transitions all the same. Notice that all the JavaScript examples start transitions automatically, no user interaction required.
So, in conclusion, transitions are about state change, and said state change pertains to changes in style property values. These are independent of selectors or JavaScript, although selectors are a fundamental part of CSS and you do need JavaScript for most things.

Is it more performant to defer CSS to :hover if possible?

Consider the following:
.element {
cursor: pointer;
}
.element:hover {
cursor: pointer;
}
To me, these two CSS snippets are identical. The cursor is only seen when hovering the element. Either method of decorating the cursor works equally well.
I'm curious how this works under the hood. Is there a preference here for using one or the other? If so, why?
Let's do some analysis here. The only difference is the hover pseudo-element, which declaratively indicates to change the rendering in response to user actions.
The cursor property description in the CSS3 spec contains the following:
This property specifies the type of cursor to be displayed for the
pointing device when over the element's border, padding, and content.
This can be interpreted as modifying the cursor as a response to a mouseover event.
On the other end the :hover pseudo selector description in the CSS3 Spec contains the following:
A visual user agent could apply this pseudo-class when the cursor (mouse pointer) hovers over a box generated by the element.
This applies a whole set of properties to the element in question as a response to a mouseover event, which could mean repainting some of the portions of the screen if you had some other properties in there, but since you are only setting the mouse cursor a repaint should not happen, so using hover with cursor can be considered as syntactic over-complication for your specific example, since they do exactly the same.
For your concrete example there should not be any significant performance difference since in both cases a mouse over event is raised, and an event handler in native code applies the new cursor. A repaint does not happen.
As I see, you only need the :hover if you want more actions or different pointers than the default when hovering the item, like these:
.element.crosshair {
cursor: crosshair;
}
.element.help {
cursor: help;
}
.element.wait {
cursor: wait;
}
The difference as per performance is too minor to even matter.
The cursor only could be seen once over anyway.
I think it's best to apply the cursor style to the element.
Check this fiddle (and probably best to do so using Chrome).
Each button creates 500,000 paragraphs with no spacing between them:
JS:
function paragraphs(className) {
var s= '';
for(var i = 0 ; i < 500000 ; i++) {
s+= '<p class="'+className+'"></p>';
}
document.querySelector('div').innerHTML= s;
}
CSS:
p {
margin: 0;
}
The first button applies the cursor on the element, the second applies it on the hover pseudo class.
The cursor should not change when moving between paragraphs. However, it does when the style is applied on the pseudo class.

ng-animate with ng-class directive

You can use ng-animate with ng-class with the add and remove animations. I'm looking to make one animation in CSS3 but haven't found good examples with ng-class online. So I was wondering if people have good examples they want to share.
I am not sure what my final animation will look like, but for the purpose of this example let's say I just want to make the height of the div gradually increase when I add the class myclass.
<div ng-class="{{myclass:scopeVar}}" ng-animate="?????"></div>
**CSS**
.myclass.ng-add{??}
.myclass.ng-add-active{??}
.myclass.ng-remove{??}
.myclass.ng-remove-active{??}
Animating an ng-class addition or removal using CSS transition has 3 stages. The order of these stages are very important, I almost spent a day figuring out why a simple animation wasn't working due incorrect understanding of the order in which classes are added.
Stage 1:
classname-add/classname-remove class is added.
Unlike what someone might think, this is actually added before the class is added to/removed from the element.
This is the stage where we should add the transition property 1 as well as initial state of our animation.
Stage 2:
classname class is added or removed.
This is where you specify the eventual styles of the element. This class often has nothing to do with our animation. Remember that we are animating the addition/removal of this class. This class itself shouldn't even need to be aware that there is an animation taking place around it.
Stage 3:
classname-add-active/classname-remove-active class is added.
This is added after the class is added to/removed from the element.
This is the stage where we should specify the final state of our animation.
To see this in action, let's create a classic fade-in-out animation shown when an element's selected state changes (selected class change using ng-class).
angular.module('demo', ['ngAnimate'])
.controller('demoCtrl', function($scope) {
$scope.selected = false;
$scope.selectToggle = function() {
$scope.selected = !$scope.selected;
};
});
.item {
width: 50px;
height: 50px;
background: grey;
}
.item.selected {
/* this is the actual change to the elment
* which has nothing to do with the animation
*/
background-color: dodgerblue;
}
.item.selected-add,
.item.selected-remove {
/* Here we specify the transition property and
* initial state of the animation, which is hidden
* state having 0 opacity
*/
opacity: 0;
transition: opacity 3s;
}
.item.selected-add-active,
.item.selected-remove-active {
/* Here we specify the final state of the animation,
* which is visible having 1 opacity
*/
opacity: 1;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular-animate.js"></script>
<div ng-app="demo" ng-controller="demoCtrl">
<div class="item" ng-class="{selected:selected}"></div>
<br>
<br>
<button ng-click="selectToggle();">
{{selected? 'Unselect' : 'Select'}}
</button>
</div>
1 Why should I specify the transition in the first state, instead of just adding it to the class being toggled or a static selector on the element?, you ask.
Well to explain this, assume you need a one-directional animation, for example a fade-out animation when a fade-out class is added.
If you add transition property on the fade-out class itself, the transition stays on the element even after the animation. Which means when your final state (fade-out-add-active) is removed, the element will slowly fade-in back, so we get a fade-out-fade-in which is not what we wanted.
Undesired result
Desired result
I've found a solution to this problem so I thought I'd share it.
http://jsfiddle.net/nicolasmoise/XaL9r/1/
What's nice about this one is that it only requires two CSS classes. You can directly insert the CSS3 transition property into your base class. Unlike other ng-animate cases, I believe all the animations are done in CSS3 (no messing with the DOM like with animations with ng-include etc...).
I want to thank Ilan Frumer for the link to his answer. His solution was for animation with ng-show which is very similar but a little different from animations with ng-class. Hence why I decided to post my example.

Using Angularjs animation to change an element on hover

so this is just a sample of what I am trying to do. I want to use Angularjs to animate the opacity of the element on hover but I can't get it to do anything. I have been reading the documentation but I can't seem to figure out what is wrong. Any help is appreciated. See example below.
http://jsbin.com/AdIXIKU/1/edit
Thank you in advance
You do not need to use angular if you want transitions on hover:
.reveal-animation {
// add the vendor prefixed versions as well
transition: all 1s;
}
.reveal-animation:hover {
opacity: 0.5;
}
Should be sufficient.
If you want to animate the entering and leaving of elements, you'll need to use some more angular constructs to ensure that elements do enter and leave like this: http://jsbin.com/iwudOjAW/1/edit (contains both hover and enter/leave effects).
I have used the ng-route module and made the ng-view element enter and leave by changing the hash of the URL.

transition-FOUC in WebKit?

WebKit just kept annoying me very much: upon page load it would animate a transition from the initial , browser-default, value. I had something like
a:link {
color: black;
-webkit-transition: color 1s;
}
but it would fade in from color: blue! Other properties weren't affected by the FOUC, only the transitions.
Unfortunately it is super hard to reproduce, I couldn't manage to jsfiddle it. My (admittedly edge) case is setup like so:
a "dev" version: a bunch of <script />s and <style type="text/less" />s
a production version: scripts and styles H5BP-style concat'ed & minified (first lessced, respectively)
the FOUC would only show up in situation 2, but that stopped after I inlined the #imports of some stylesheets with #font-faces. A workaround, but it
So I guess this must have something to with load times/order?
Anybody ever encountered something like this? (I guess not.)
At the very least, maybe someday somebody will run into this problem, and find this useful.
If I had to guess, I'd say it's because you've put your transition before your color. By doing it that way, you've assigned the transition while the link is the default color (blue), then told it to change color (in theory).
Try putting your transitions last to keep them from transitioning from the browser default values.

Resources