animating addClass/removeClass with jQuery - javascript

I am using jQuery and jQuery-ui and want to animate various attributes on various objects.
For the sake of explaining the issue here I've simplified it to one div that changes from blue to red when the user mouses over it.
I am able to get the behavior I want when using animate(), however when doing so the styles I am animating have to be in the animation code and so are separate from my style sheet. (see example 1)
An alternative is using addClass() and removeClass() but I have not been able to re-create the exact behavior that I can get with animate(). (see example 2)
Example 1
Let's take a look at the code I have with animate():
$('#someDiv')
.mouseover(function(){
$(this).stop().animate( {backgroundColor:'blue'}, {duration:500});
})
.mouseout(function(){
$(this).stop().animate( {backgroundColor:'red'}, {duration:500});
});
it displays all the behaviors I am looking for:
Animates smoothly between red and blue.
No animation 'overqueue-ing' when the user moves their mouse quickly in and out of the div.
If the user moves their mouse out/in while the animation is still playing it eases correctly between the current 'halfway' state and the new 'goal' state.
But since the style changes are defined in animate() I have to change the style values there, and can't just have it point to my stylesheet. This 'fragmenting' of where styles are defined is something that really bothers me.
Example 2
Here is my current best attempt using addClass() and removeClass (note that for the animation to work you need jQuery-ui):
//assume classes 'red' and 'blue' are defined
$('#someDiv')
.addClass('blue')
.mouseover(function(){
$(this).stop(true,false).removeAttr('style').addClass('red', {duration:500});
})
.mouseout(function(){
$(this).stop(true,false).removeAttr('style').removeClass('red', {duration:500});
});
This exhibits both property 1. and 2. of my original requirements, however 3 does not work.
I understand the reason for this:
When animating addClass() and removeClass() jQuery adds a temporary style to the element, and then increments the appropriate values until they reach the values of the provided class, and only then does it actually add/remove the class.
Because of this I have to remove the style attribute, otherwise if the animation is stopped halfway the style attribute would remain and would permanently overwrite any class values, since style attributes in a tag have higher importance than class styles.
However when the animation is halfway done it hasn't yet added the new class, and so with this solution the color jumps to the previous color when the user moves their mouse before the animation is completed.
What I want ideally is to be able to do something like this:
$('#someDiv')
.mouseover(function(){
$(this).stop().animate( getClassContent('blue'), {duration:500});
})
.mouseout(function(){
$(this).stop().animate( getClassContent('red'), {duration:500});
});
Where getClassContent would just return the contents of the provided class. The key point is that this way I don't have to keep my style definitions all over the place, but can keep them in classes in my stylesheet.

Since you are not worried about IE, why not just use css transitions to provide the animation and jQuery to change the classes. Live example: http://jsfiddle.net/tw16/JfK6N/
#someDiv{
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}

Another solution (but it requires jQueryUI as pointed out by Richard Neil Ilagan in comments) :-
addClass, removeClass and toggleClass also accepts a second argument; the time duration to go from one state to the other.
$(this).addClass('abc',1000);
See jsfiddle:- http://jsfiddle.net/6hvZT/1/

You could use jquery ui's switchClass, Heres an example:
$( "selector" ).switchClass( "oldClass", "newClass", 1000, "easeInOutQuad" );
Or see this jsfiddle.

You just need the jQuery UI effects-core (13KB), to enable the duration of the adding (just like Omar Tariq it pointed out)

I was looking into this but wanted to have a different transition rate for in and out.
This is what I ended up doing:
//css
.addedClass {
background: #5eb4fc;
}
// js
function setParentTransition(id, prop, delay, style, callback) {
$(id).css({'-webkit-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'-moz-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'-o-transition' : prop + ' ' + delay + ' ' + style});
$(id).css({'transition' : prop + ' ' + delay + ' ' + style});
callback();
}
setParentTransition(id, 'background', '0s', 'ease', function() {
$('#elementID').addClass('addedClass');
});
setTimeout(function() {
setParentTransition(id, 'background', '2s', 'ease', function() {
$('#elementID').removeClass('addedClass');
});
});
This instantly turns the background color to #5eb4fc and then slowly fades back to normal over 2 seconds.
Here's a fiddle

Although, the question is fairly old, I'm adding info not present in other answers.
The OP is using stop() to stop the current animation as soon as the event completes. However, using the right mix of parameters with the function should help. eg. stop(true,true) or stop(true,false) as this affects the queued animations well.
The following link illustrates a demo that shows the different parameters available with stop() and how they differ from finish().
http://api.jquery.com/finish/
Although the OP had no issues using JqueryUI, this is for other users who may come across similar scenarios but cannot use JqueryUI/need to support IE7 and 8 too.

Related

Different behavior of transition when launched via event listener vs direct function call [duplicate]

I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.
Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.
Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.
Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?
I've put together a little example of what the code is right now: http://g2f.nl/38rvma
Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/
To make transition work, three things have to happen.
the element has to have the property explicitly defined, in this case: opacity: 0;
the element must have the transition defined: transition: opacity 2s;
the new property must be set: opacity: 1
If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:
window.setTimeout(function() {
slides[targetIndex].className += " target-fadein";
}, 100);
Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.
Adding transition to an element is not what triggers the animation, changing the property does.
// Works
document.getElementById('fade1').className += ' fade-in'
// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'
// Works
document.getElementById('fade3').className = 'fadeable'
window.setTimeout(function() {
document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
opacity: 0;
}
.fade-in {
opacity: 1;
transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>
Trick the layout engine!
function finalizeAndCleanUp (event) {
if (event.propertyName == 'opacity') {
this.style.opacity = '0'
this.removeEventListener('transitionend', finalizeAndCleanUp)
}
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'
As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.
Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.
Cleanup code added for completeness.
Some people have asked about why there is a delay. The standard wants to allow multiple transitions, known as a style change event, to happen at once (such as an element fading in at the same time it rotates into view). Unfortunately it does not define an explicit way to group which transitions you want to occur at the same time. Instead it lets the browsers arbitrarily choose which transitions occur at the same time by how far apart they are called. Most browsers seem to use their refresh rate to define this time.
Here is the standard if you want more details:
http://dev.w3.org/csswg/css-transitions/#starting

How Can I Animate Background Color Dynamically Added Element?

I am adding some elements with ajax. For this example, i am adding user comments. When user make new comment, his comment viewing first place and (this is question) I want to add new background animation first comment div. But i know we cant animate color with Jquery. I can use JqueryUI highlight effect. But i cant select new added element. Off course use here delegate() or on().. But my all try failed.
I need help.
Some Code Example;
$("#DoCommentBtn").click(function(){
var UserID = 1,
LookID = 2,
Comment = $("#CommentInput").val(),
CommentType = 0,
PostData = "USERID=" + UserID + "&LOOKID=" + LookID + "&Comment=" + encodeURI(Comment) + "&CommentType=" + CommentType;
$("#CmAnm").fadeIn();
$.ajax({
type:'POST',
url:'ajax.asp?cmd=docomment',
dataType:'html',
data:PostData,
success:function(cevap) {
$("#CmAnm").fadeOut();
$("#CommentsArea").load("ajax.asp?cmd=LoadComments&LOOKID=" + LookID + "&PART=0");
//Now all comments loaded Here I must animate background first div in #CommentsArea Animation Example: Background color blue to white..
}
});
});
But i know we cant animate color with Jquery
I'm not a jquery expert but i'm sure it is possible.
Is this what you're looking for?
$( "#CommentsArea div:first-child" ).animate({ backgroundColor: "#fff"}, 1000);
Should animate the background to white #FFF in 1 second (1000ms)
Docs: jQuery .animate() - jQuery UI .animate()
EDIT:
changed background to backgroundColor
Animation Properties and Values
All animated properties should be animated to a single numeric value, except as noted below; most properties that are non-numeric cannot be animated using basic jQuery functionality
(For example, width, height, or left can be animated but background-color cannot be, unless the jQuery.Color plugin is used)
Docs : JQuery animate()
So what can i do right now ?
I am adding this library;
Jquery.Color
And now jsfiddle example working.
Example Code :
$(".justDiv").animate({ backgroundColor: "#ffffff",width:105 }, 1000);

Jquery mouseover & mouseout animation bug

I have a small mouseover and mouseout functionality i have to keep the mouseout function animate() instead of css() due to other reasons
the problem arises when i mouseover when the animation from opacity 1 to 0 is still going on like a quick mouseover mouseout like we do for testing.
i tried setTimeOut too so that the opacity is zeroed after required time.
both animate and setTimeOut are creating the same problem that after mouserover function updates the opacity to 1 the animate and setTimeOut are updating again to zero since they are still playing.
JSFIDDLE
Jquery Code:
$("#dp-ashish").on("mouseover",function(){
$("#dp-ashish").css("opacity","1");
});
$("#dp-ashish").on("mouseout",function(){
$("#dp-ashish").animate({"opacity":"0"},1000);
});
You might want to consider using either .stop(true, true) or .finish() (the latter only works in jQuery v1.9 and above):
$("#dp-ashish").on("mouseover",function(){
$("#dp-ashish").stop(true,true).animate({opacity:1},1000);
});
$("#dp-ashish").on("mouseout",function(){
$("#dp-ashish").stop(true,true).animate({opacity:0},1000);
});
or:
$("#dp-ashish").on("mouseover",function(){
$("#dp-ashish").finish().animate({opacity:1},1000);
});
$("#dp-ashish").on("mouseout",function(){
$("#dp-ashish").finish().animate({opacity:0},1000);
});
p/s: The opacity and its numerical integer value do not need to be wrapped in quotes. Opacity is a valid property without needing to be parsed as a string, and its accepted values are integers which do not need to be passed as a string, too.
Alternatively, you can toggle a CSS class and let CSS transitions handle the opacity change :)
http://jsfiddle.net/teddyrised/5Ekbf/4/

CSS animation won't trigger on addClass in jQuery

I have a very strange issue. I'm loading articles from JSON in jQuery and as they load, I'd like to add a class of 'animate' to each dynamic element.
$.each(jsonArticles, function (i, article) {
var $articleHTML = $(
'<article class="article">' +
'<img src="' + jsonObject.imagePath + article.reviewImage + '" alt="">' +
'<h1>' + article.reviewTitle + '</h1>' +
'<p>' + article.reviewSummary + '</p>' +
'</article>');
$articles
.append($articleHTML)
.find("article")
.addClass("animate");
});
All of this works great and checking in Firebug reveals that the class is successfully added to each article tag.
However, when trying to use a CSS transition on the article for the class that's added, it does not animate, but instead skips straight to the final style (opacity: 1).
.article {
opacity: 0;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.article.animate {
opacity: 1;
}
The animation doesn't happen, but the class is added and the article is successfully set to opacity: 1. It shows up instantly.
Anyone have any ideas about this? I cannot figure this one out at all.
On another point, which is rather interesting...if I change the .animate class to have a :hover, then the articles won't show until I hover and the animation does work. Why it would work for hover and not when it's simply added immediately, seems strange to me.
.article.animate:hover {
opacity: 1;
}
I'd appreciate any input.
Thanks,
Mikey.
Live Demo: http://jsfiddle.net/Pz5CD/
Notice how the articles just pop in at 100% opacity. No animation is seen.
Update:
It turns out the OP wants to fade in each element sequentially, which is beyond the scope of the original question. I'll leave my answer here as an answer to the original question.
CSS animation won't trigger on addClass in jQuery
The issue is that your new html is added to the page and the animate class is added before the css for that html has been applied. The browser will skip ahead like that for the sake of efficiency. For example, if you added a class, then removed it, and repeated that process a hundred times, there wouldn't be a visual difference. It would have just skipped to the result. For this reason, you have to force a redraw on the element so that all previous styles have applied before adding the class. I wrote a function to handle this that should work in every circumstance on every browser, though there's no way to guarantee the behavior of a reDraw. It probably will always work and it's nice to have!
Live demo here (click). You can tell the reDraw is making the difference by commenting it out and just leaving the addClass().
$('body').append($articleHTML);
$a = $('body').find("article");
reDraw($a).then(function() {
$a.addClass("animate");
});
function reDraw($element) {
var deferred = new $.Deferred();
setTimeout(function() {
var h = $element[0].offsetHeight;
var s = $element[0].getComputedStyle;
deferred.resolve();
},0);
return deferred.promise();
}
The best way to force a redraw is to either access the offsetHeight or getComputedStyle of an element. However, there have been cases where those have failed for force a redraw on certain mobile devices. To add some extra encouragement for a redraw, I added a setTimeout as well. Even a time of 0 on the timeout will work, but it throws off the call stack, so I use a promise to ensure the next operation (adding your class) will happen after the redraw. That just means you'll use the syntax I demonstrated above to add the class - redraw($element).then(function() { //your code
For fun, I made a little demo of flipping classes with and without reDraw. http://jsbin.com/EjArIrik/1/edit
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/

jQuery animate problems

I use the following snippet to make an element's background lightblue, then slowly fade to whiite over 30 seconds:
$("#" + post.Id).css("background-color", "lightblue")
.animate({ backgroundColor: "white" }, 30000);
Two questions.
First, instead of fading to white, is there a way to fade opacity to 100%? That way I don't have to change "white" if I choose to change the page's background color?
Second, about once out of every 10 or 15 times, the background stays lightblue and fails to fade to white. I'm using the latest versions of jQuery and the UI core. What could be going wrong?
EDIT: Bounty is for a solution to problem regarding second question.
EDIT2:
Apparently I got downvoted into oblivion because I said I rolled my own solution but didn't show it. My bad. I didn't want to be self-promoting. My code works 100% of the time and doesn't require jQuery. A demonstration and the code can be found at:
http://prettycode.org/2009/07/30/fade-background-color-in-javascript/
For your second question: in my experience this is usually because a Javascript error has occurred somewhere else on the page. Once there is one Javascript exception, the rest of the page stops running Javascript. Try installing Firebug (if you haven't already), then open up the "Console" tab and enable it. Then any javascript errors or exceptions will be printed to the console.
Another thing to try (which kinda contradicts my last statement...) is to disable all your browser plug-ins to see if you can recreate. Sometimes they interfere with scripts on the page (particularly GreaseMonkey.)
If you could provide a sample HTML snippet which reproduces this animation problem it would be a lot easier for us to help you. In the script I have pasted below, I can click it all day, as fast or slow as I like, and it never fails to animate for me.
For the first question: I know you said you'd found a workaround, but the following works for me (even on IE6) so I thought I'd post it, since it may be different from what you were thinking. (Note that setting CSS "opacity" property through jQuery.css() works on IE, whereas IE does not support the "opacity" property directly in CSS.)
<html>
<head>
<style>
body { background-color: #08f; }
#test { background-color: white; width: 100px; }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
var myOpacity = 0.125;
$(function(){
$('#test').css('opacity', myOpacity);
$('a').click(function(){
myOpacity = 1.0 - myOpacity;
$('#test').animate({ opacity: myOpacity });
return false;
});
});
</script>
</head>
<body>
<p>Click me</p>
<div id="test">Test</div>
</body></html>
Dont forget the color plugin.
See here
When the color fails to animate to blue you could try to use the callback function to log a message to the console. You can then check that the event actually fired and completed. If it does then you could potentially use two animates. The first one to animate to a halfway house color then the use the callback to animate to white (so you get two bites of the cherry, if the outer fails but completes the callback has a second go)
It would be good if you could try to recreate the issue or give a url of the issue itself.
e.g
$("#" + post.Id).css("background-color", "lightblue")
.animate({ backgroundColor: "#C0D9D9" }, 15000, function(){
$(this).animate({ backgroundColor: "#ffffff" }, 15000)
});
You could always use something like this, avoiding the JQuery animate method entirely.
setTimeout(function() { UpdateBackgroundColor(); }, 10);
UpdateBackgroundColor() {
// Get the element.
// Check it's current background color.
// Move it one step closer to desired goal.
if (!done) {
setTimeout(UpdateBackgroundColor, 10);
}
}
Also, you may be able to remove the "white" coding by reading the background color from the appropriate item (which may involve walking up the tree).
It is possible to have jQuery change the Opacity CSS property of an item (as mentioned in another answer), but there's two reasons why that wouldn't work for your scenario. Firstly, making something "100% opaque" is fully visible. If the item didn't have any other modifications to its opacity, the default opacity is 100%, and there would be no change, so I'm guessing you meant fading to 0% opacity, which would be disappearing. This would get rid of the light blue background, but also the text on top of it, which I don't think was your intent.
A potentially easy fix for your situation is to change the color word "white" to "transparent" in your original code listing. The color plugin may not recognize that color word (haven't checked documentation on that yet), but setting the background color to "transparent" will let whatever color behind it (page background, if nothing else) shine through, and will self-update if you change your page background.
I'll answer your first question.
You can animate opacity like this:
.animate({opacity: 1.0}, 3000)
I think you can try using fadeOut/fadeIn too..
What about:
$("#" + post.Id).fadeIn( "slow" );
You could possibly have two divs that occupy the same space (using position: absolute; and position: relative; setting the z-index on one higher to make sure one is above and the other is below. the top one would have a transparent background and the one below would have a background color. then just fadeout the one below.
As for the second question:
If you think the default animation classes from JQuery are not properly working you could try Bernie's Better Animation Class. I have some good experiences with that library.
Animate only works for numbers. See the jquery docs. You can do opacity but you can't do background color. You can use the color plug in. Background-color uses strings like 'red', 'blue', '#493054' etc... which are not numbers.

Categories