Displaying/Hidding chained CSS3 animations with jQuery on Mouseenter/Mouseleave - javascript

I am playing a bit with animate.css and jQuery on a bootstrap-based environment.
But I have a big problem!
I want to trigger animations on mouseenter / mouseleave, so I chained some complex set of callbacks, and now It's driving me crazy.
Look at my jQuery (no-conflict mode because of other plugins):
jQuery(document).ready(function () {
var animationend = "webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend";
var imgRotacion = "animated rotateOut";
var h3Bounce = "animated bounceIn";
var pFlip = "animated flipInX";
var imgRotacionOff = "animated rotateIn";
var h3BounceOff = "animated bounceOut";
var pFlipOff = "animated flipOutX";
jQuery(".procaption").on("mouseenter", function () {
jQuery(this).find("img").addClass(imgRotacion).one(animationend, function () {
jQuery(this).hide();
jQuery(this).parent().find("h3").removeClass("hidden").addClass(h3Bounce).one(animationend, function () {
jQuery(this).parent().find("p").removeClass("hidden").addClass(pFlip);
});
});
});
jQuery(".procaption").on("mouseleave", function () {
jQuery(this).find("p").removeClass().addClass(pFlipOff).one(animationend, function () {
jQuery(this).removeClass().addClass("hidden");
jQuery(this).parent().find("h3").removeClass().addClass(h3BounceOff).one(animationend, function () {
jQuery(this).removeClass().addClass("hidden");
jQuery(this).parent().find("img").removeClass(imgRotacion).show().addClass(imgRotacionOff);
});
});
});
});
The HTML is pretty simple:
<div class="procaption wow fadeInLeft well text-center">
<img src="holder.js/150x150" alt="150x150" class="img-responsive img-circle center-block">
<h3 class="hidden">This is a title</h3>
<p class="hidden">But this is a description!</p>
</div>
The behavior I want to achieve:
Well, I want to chain all the animations, so they appear and disappear in some kind of order at mouseenter and mouseleave events.
Actually, it's "working" but only when mouseleaveis triggered after the last animation of mouseenter have happened.
If I try to mouseenterand instantly mouseleave, the <p>But this is a description!</p> line appears along with the <img>... That shouldn't happen!
Here's the jsFiddle.
I'm sure there should be some easier way, but I'm just learning and practicing... So any suggestion will be really appreciated!
Just for the record, I tried changing the .procaption with .procaptioningduring the animation, until the last callback is complete, but didn't work :(
I also tried $(".procaptioning").on("mouseenter mouseleave", function() { return false; }) ... without success.

Your .removeClass() functions are messing with things since they are not targeting anything. Essentially, hovering on and off quickly will cause overlapping functions to create unexpected results without specifying which classes to remove at which times. I've also made the code clearer by reducing nested this usage and using consistent selectors. See this FIDDLE for working example.
function onHoverIn(e) {
var $caption = jQuery(this),
$image = $caption.find('img'),
$h3 = $caption.find('h3'),
$desc = $caption.find('p');
$image.addClass(imgRotacion).one(animationend, function () {
$image.addClass('hidden');
$h3.removeClass('hidden').addClass(h3Bounce).one(animationend, function () {
$desc.removeClass(pFlipOff + ' hidden').addClass(pFlip);
});
});
}
function onHoverOut(e) {
var $caption = jQuery(this),
$image = $caption.find('img'),
$h3 = $caption.find('h3'),
$desc = $caption.find('p');
$desc.removeClass(pFlip).addClass(pFlipOff).one(animationend, function () {
$desc.removeClass(pFlipOff).addClass('hidden');
$h3.removeClass(h3Bounce).addClass(h3BounceOff).one(animationend, function () {
$h3.removeClass(h3BounceOff).addClass('hidden');
$image.removeClass(imgRotacion).removeClass('hidden').addClass(imgRotacionOff);
});
});
}
jQuery(".procaption").hover(onHoverIn, onHoverOut);

Related

How can I put two functions (one must delay) in one onclick button?

I have a problem to combine to functions in one onclick button. This is how my product have to look like: I want to put a hammer down when I click on an invisible button, when the hammer reaches an icon, the icon has te become another picture.
This is the code I have, but it's in javascript and jquery:
$('.box hammer').on('click', function() {
$(this).toggleClass('clicked');
});
function changeImage1() {
var image = document.getElementById('safari');
if (image.src.match("bulbon")) {
image.src = "safari.png";
} else {
image.src = "safariflat.png";
}
}
You can bind multiple on handlers to the same element and event, that's not a problem.
Delaying one of those handlers can be done in many ways, how is up to you. Libraries like Underscore and lodash offer a number of delay types, such as debounce.
You should be able to use something like:
function immediateHandler() {
alert("Immediate handler!");
}
function lateHandlerImpl() {
alert("Late handler!");
}
var lateHandler = _.delay(lateHandlerImpl, 2500);
$('div.button').on('click', immediateHandler);
$('div.button').on('click', lateHandler);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="button">Click Me!</div>
Replace the alerts with your logic (creating/switching the images) and change the timing to sync the image changes up, and you should be able to create a fairly convincing animation.
You could use setTimeout for this:
$('.box hammer').on('click', function() {
$(this).toggleClass('clicked');
setTimeout(function() {
changeImage1();
// change time below to the time your animation takes
}, 5000);
});
function changeImage1() {
var image = document.getElementById('safari');
if (image.src.match("bulbon")) {
image.src = "safari.png";
} else {
image.src = "safariflat.png";
}
}

Need Help for better practice Jquery codes

I am trying to make my jquery codes look better here. My functions are working correctly but I was wondering if anyone can make my codes less ugly. Thanks a lot!
HTML
<div class='image_layout'>
<a href='#'><img src=' a.jpg '/></a>
<br><p class='credits'>hahahah
<br>Agency: Agency1
<br>Picture ID: 5 </p>
</div>
jQuery
$('#image_layout').on('hover', 'img', function() {
$(this).parent().next().next().fadeIn('fast');
})
$('#image_layout').on('mouseout', 'img', function() {
$(this).parent().next().next().fadeOut('fast');
})​
You can pass two functions to jQuery hover - one for mousein, one for mouseout. You can make this change as long as you don't have dynamically added images. Your code would also be a lot simpler if the element you are fading has an ID or class:
$('#image_layout img').hover(
function () {
$(this).closest('.someClass').fadeIn('fast');
},
function () {
$(this).closest('.someClass').fadeOut('fast');
}
);
$('.image_layout').on('hover', 'img', function (e) {
if(e.type == 'mouseover') {
$(this).closest('.image_layout').find('.credits').stop().fadeIn('fast');
} else {
$(this).closest('.image_layout').find('.credits').stop().fadeOut('fast');
}
})
You could also have done:
$('.image_layout').on('hover', 'img', function() {
$(this).closest('.image_layout').find('.credits').stop().fadeIn('fast');
}, function() {
$(this).closest('.image_layout').find('.credits').stop().fadeOut('fast');
});
If you're sure that nothing other than hovering the image will cause the element to fade, you could simply write:
$('.image_layout').on('hover', 'img', function() {
$(this).closest('.image_layout').find('.credits').stop().fadeToggle('fast');
});
Look into Douglas Crockford's JS Style Guide. He'd make your code look something like (with improvements):
var obj = $('#image_layout img');
obj.mouseover( function(){
$(this).parent([selector]).next([selector]).fadeIn('fast');
});
obj.mouseout( function(){
$(this).parent([selector]).next([selector]).fadeOut('fast');
});
You don't need the on, just call the function directly.
I would use .eq as opposed to two next statements, additionally, hover takes two functions, the first being for the mouseenter event, and the second for mouseout
$('#image_layout').hover('hover', 'img', function () {
$(this).parent().eq(2).fadeIn('fast');
}, function () {
$(this).parent().eq(2).fadeOut('fast');
})
References
Take a look at eq here
Read over hover here

Add smoothness between changing images with jQuery

I have this code that changes the image when a user rolls over a map area on my United States map. It is working perfectly. But I want the the images to have a smooth appearance and then a gradual fade away. What do I need to add to this code? Thanks!
$(document).ready(function() {
//set off state
var nav_off = "/images/state-map.png";
// functions for over and off
function over(image) {
$("#main-nav").attr("src", image);
}
function off() {
$("#main-nav").attr("src", nav_off);
}
$("#imagemap area").hover(
function () {
var button = $(this).attr("id");
over("/images/state-" + button + ".png");
},
function () {
off();
});
});
Try using jQuery's fadeOut/fadeIn effect.
Perhaps something like this:
function over(image) {
$("#main-nav").fadeOut(function () {
$(this).attr("src", image).fadeIn();
});
}
function off() {
$("#main-nav").fadeOut(function () {
$(this).attr("src", nav_off).fadeIn();
});
}
CSS transitions are an alternative way of handling this. There's a good tutorial (and demo) here.
you can see the effects API .using fadeIn() fadeOut() functions
http://api.jquery.com/category/effects/

Timer Reset Function Not Working!

Hello Guys!
I have been trying to create a simple sample code for my newest jQuery Plugin, but it doesn't seems to be working at all! Can anyone tell where I'm going wrong?, or can anyone provide me a new function to do it. So my problem is that when I mouse over an element classed trigger an another element classed eg should fadeIn(); but if the user takes out the mouse before the element classed eg fades in it should not be fading in anymore, but this is not working at all. I don't not what is getting wrong? Please help me out. (Below is my Problem HTML nad Jquery Code!)
HTML CODE
<div class="trigger">MouseOverMe</div>
<div class="eg">See Me!</div>
JQUERY CODE
function timereset(a)
{
var elem = $('.'+a);
if(elem.data('delay')) { clearTimeout(elem.data('delay')); }
}
$(document).ready(function () {
$('div.eg').hide();
$('div.trigger').mouseover(function () {
$('div.eg').delay(1000).fadeIn();
});
$('div.trigger').mouseout(function () {
timereset('eg');
$('div.eg').fadeOut();
});
});
THANKS IN ADVANCE
You don't need that timereset stuff, simply call stop() on the object and the previous effect will stop:
http://api.jquery.com/stop/
Update based on the new comment:
$('div.trigger').mouseout(function () {
$('div.eg').stop().hide();
});
jQuery
$('.trigger').hover(function() {
$('.eg').delay(1000).fadeIn();
}, function() {
$('.eg').stop(true, true).hide();
});
Fiddle: http://jsfiddle.net/UJBjg/1
Another option would be to clear the queued functions like:
$('div.trigger').mouseout(function () {
$('div.eg').queue('fx', []);
$('div.eg').fadeOut();
});
Bear in mind if the fadeOut/In has already started by using stop you could end up with a semi-transparent element.
EDIT
Here's an example: http://jsfiddle.net/Qchqc/
var timer = -1;
$(document).ready(function () {
$('div.eg').hide();
$('div.trigger').mouseover(function () {
timer = window.setTimeout("$('div.eg').fadeIn(function() { timer = -1; });",1000);
});
$('div.trigger').mouseout(function () {
if(timer != -1)
window.clearTimeout(timer);
$('div.eg').fadeOut();
});
});

Unintuitive removeClass() problem

I'm using this flip plugin, see the code in this fiddle. The goal is to flip one box at a time, e.g. the second box clicked should revertFlip() the previous one. During the animation I don't want the other boxes to be clickable. I noted that the removeClass() is not working.
<div class='flippable'>I'm unflipped 1</div>
...
<div class='flippable'>I'm unflipped 9</div>
$('.flippable:not(.reverted)').live('click',function()
{
var $this = $(this);
var $prevFlip = $('.reverted');
var $allBoxes = $('.flippable');
$this.flip(
{
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onBefore: function()
{
$prevFlip.revertFlip();
$prevFlip.removeClass('reverted');
},
onAnimation: function ()
{
$allBoxes.preventDefault();
},
onEnd: function()
{
$this.addClass('reverted');
}
})
return false;
});
I'll appreciate a lot your advise and suggestions.
Edit:
Error Console Output: $allBoxes.preventDefault is not a function
I believe this has something to do with revertFlip() calling onBefore and onEnd. This is causing some weirdness with addClass and removeClass. Check out my modified example: http://jsfiddle.net/andrewwhitaker/7cysr/.
You'll see if you open up FireBug that onBefore and onEnd are called multiple times, with I think is having the following effect (I haven't exactly worked out what's going on):
The call to onEnd for the normal "flip" sets reverted class for the desired element.
The call to onEnd for the "revert flip" action sets the same element as above again when onEnd is called.
Here's a workaround. I've taken out using onBegin and simplified onEnd since I'm not exactly sure what's going on with the revertFlip() call:
$(function() {
var handlerEnabled = true;
$('.flippable:not(.reverted)').live('click', function() {
if (handlerEnabled) {
var $this = $(this);
var $prevFlip = $('.reverted');
var $allBoxes = $('.flippable');
handlerEnabled = false;
$prevFlip.revertFlip();
$prevFlip.removeClass("reverted");
$this.addClass("reverted");
$this.flip({
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onEnd: function() {
handlerEnabled = true;
}
});
}
return false;
});
})
I'm using a boolean flag to enable and disable the event listener. Try out this example: http://jsfiddle.net/andrewwhitaker/bX9pb/. It should work as you described in your OP (only flipping one over at a time).
Your original code ($allBoxes.preventDefault()) is invalid, because $allBoxes is a collection of elements. preventDefault is a function on the jQuery event object.
Can you try this script
var $prevFlip;
$('.flippable:not(.reverted)').live('click',function() {
var $this = $(this);
var $allBoxes = $('.flippable');
$this.flip( {
direction: 'lr',
color: '#82BD2E',
content: 'now flipped',
onBefore: function() {
if($prevFlip){
$prevFlip.revertFlip();
$prevFlip.removeClass('reverted');
}
},
onAnimation: function () {
//$allBoxes.preventDefault();
//This is not a valid line
},
onEnd: function() {
$this.addClass('reverted');
$prevFlip = $this;
}
});
return false;
});
This reverts only one previous item. This is not a complete solution. I think there are more problems to this. I'll post any further updates if I found them.
I think #Andrew got the answer you are looking for.
You can filter $this out of $allBoxes by using the not() method.
$allBoxes.not($this).revertFlip();

Categories