I have written a small amount of code to try and replicate jQuery's .fadeIn()
and .fadeOut()
functions using CSS transitions to look better on touch devices.
Ideally I'd like to avoid using a library so that I can write exactly what I want, and as a learning exercise.
fadeOut
works well.
The idea for fadeIn
is to use CSS3 transitions to adjust the opacity of an element, after which the element will be set to display:block;
(using is-hidden
class) to ensure it's not still clickable or covering something beneath it.
fadeIn
is not working though. I think it is due to adding the is-animating
class at the same time as removing the is-hiding
class. The transitionEnd event never fires because a transition does not occur:
function fadeIn (elem, fn) {
var $elem = $(elem);
$elem.addClass('is-animating');
$elem.removeClass('is-hidden');
$elem.removeClass('is-hiding');
$elem.on(transitionEndEvent, function () {
$elem.removeClass('is-animating');
if (typeof fn === 'function') {
fn();
}
});
};
And the CSS
.is-animating {
@include transition(all 2000ms);
}
.is-hiding {
// Will transition
@include opacity(0);
}
.is-hidden {
// Won't transition
display: none;
}
Here's the code: CodePen link
Update: I have found what I'd describe as a hack, but that works very well: CSS3 replacement for jQuery.fadeIn and fadeOut
Working code after this fix: Fixed
A solution without setTimeout
would be very valuable though.
I have written a small amount of code to try and replicate jQuery's .fadeIn()
and .fadeOut()
functions using CSS transitions to look better on touch devices.
Ideally I'd like to avoid using a library so that I can write exactly what I want, and as a learning exercise.
fadeOut
works well.
The idea for fadeIn
is to use CSS3 transitions to adjust the opacity of an element, after which the element will be set to display:block;
(using is-hidden
class) to ensure it's not still clickable or covering something beneath it.
fadeIn
is not working though. I think it is due to adding the is-animating
class at the same time as removing the is-hiding
class. The transitionEnd event never fires because a transition does not occur:
function fadeIn (elem, fn) {
var $elem = $(elem);
$elem.addClass('is-animating');
$elem.removeClass('is-hidden');
$elem.removeClass('is-hiding');
$elem.on(transitionEndEvent, function () {
$elem.removeClass('is-animating');
if (typeof fn === 'function') {
fn();
}
});
};
And the CSS
.is-animating {
@include transition(all 2000ms);
}
.is-hiding {
// Will transition
@include opacity(0);
}
.is-hidden {
// Won't transition
display: none;
}
Here's the code: CodePen link
Update: I have found what I'd describe as a hack, but that works very well: CSS3 replacement for jQuery.fadeIn and fadeOut
Working code after this fix: Fixed
A solution without setTimeout
would be very valuable though.
4 Answers
Reset to default 1i don't know what you really wanna achieve but if your using css3 your using a modern browser. in that case pure css & javascript is a better solution.
it's all about how you write the css transition.
here is the js code
var div=document.getElementsByTagName('div')[0],
btn=document.getElementsByTagName('button')[0];
div.addEventListener('click',function(){
this.classList.add('hide');
},false);
div.addEventListener('webkitTransitionEnd',function(e){
console.log(e.propertyName);
},false);
btn.addEventListener('click',function(e){
div.classList.toggle('hide');
},false);
css code
div{
width:200px;height:200px;
opacity:1;
overflow:hidden;
line-height:200px;
text-align:center;
background-color:green;
-webkit-transition:opacity 700ms ease 300ms,height 300ms ease ;
}
div.hide{
height:0px;
opacity:0;
-webkit-transition:opacity 700ms ease,height 300ms ease 700ms;
/*add the various -moz -ms .. prefixes for more support*/
}
and the html
some text
<div>click here</div>
some text
<button>toggle</button>
here is an example.
http://jsfiddle/qQM5F/1/
Alternative solution using Keyframes
js
var div=document.getElementsByTagName('div')[0],
btn=document.getElementsByTagName('button')[0];
div.addEventListener('webkitAnimationEnd',function(e){
div.style.display=div.classList.contains('hide')?'none':'';
},false);
btn.addEventListener('click',function(e){
div.style.display='';
div.classList.toggle('hide');
},false);
css3
div{
background-color:green;
-webkit-animation:x 700ms ease 0ms 1 normal running;/*normal*/
opacity:1;
}
div.hide{
-webkit-animation:x 700ms ease 0ms 1 reverse running;/*reverse*/
opacity:0;
}
@-webkit-keyframes x{
0%{opacity:0;}
100%{opacity:1;}
}
example
http://jsfiddle/qQM5F/8/
here is a prototype
Object.defineProperty(HTMLElement.prototype,'toggleOpacity',{value:function(){
function check(e){
this.style.display=this.classList.contains('hide')?'none':'';
this.removeEventListener('webkitAnimationEnd',check,false);// clean up
}
this.addEventListener('webkitAnimationEnd',check,false);
this.style.display='';
this.classList.toggle('hide');
},writable:false,enumerable:false});
css
.fade{
-webkit-animation:x 700ms ease 0 1 normal;
opacity:1;
}
.fade.hide{
-webkit-animation:x 700ms ease 0 1 reverse;
opacity:0;
}
@-webkit-keyframes x{
0%{opacity:0}
100%{opacity:1}
}
usage
the element you need to fade needs a class fade
then toggle it with
element.toggleOpacity();
example
http://jsfiddle/qQM5F/9/
You may want to consider a couple of plugins that might take care of what you want:
jQuery.transition.js retrofits the existing jQuery animation methods to use CSS transitions in browsers that support them.
Transit adds a
transition
function you can use to define your own transitions. It uses jQuery's effect queue, so you can queue up the changeddisplay
value to run afteropacity
has finished transitioning.
I have managed to fix this by doing something that feels unnatural and hacky:
function fadeIn (elem, fn) {
var $elem = $(elem);
$elem.addClass('is-animating');
$elem.removeClass('is-hidden');
// Smelly, setTimeout fix
setTimeout(function () {
$elem.removeClass('is-hiding');
}, 0);
$elem.on(transitionEndEvent, function () {
$elem.removeClass('is-animating');
if (typeof fn === 'function') {
fn();
}
});
};
Adding the setTimeout
function to the class that contains the transition-able property fixes the issue.
Working code here: Codepen fixed code