(jQuery) Issue with fading in/out elements within .hover() - javascript

I'm having an issue trying to get .fadeIn(), .fadeOut(), and .hide() to behave properly when an element is hovered over.
Let's say I have a container, .post-box.
.post-box contains two divs: .description and .quote. The .quote div is initially hidden so that when .post-box is hovered over, it fades in and takes the place of the .description div, which gets hidden with .hide().
When .post-box is hovered out of, .quote fades out and .description is faded in again.
$(document).ready(function() {
var description = $('.description');
var quote = $('.quote');
var postBox = $('.post-box');
postBox.hover(function() {
$(this).find(description).clearQueue().stop(true, true).hide(0, function() {
quote.fadeIn(300);
});
}, function() {
$(this).find(quote).clearQueue().stop(true, true).fadeOut(300, function() {
description.fadeIn(300);
});
});
});
It seems that I've got this concept working fairly well except for one issue. If you quickly hover over .post-box, quickly hover out, and then quickly hover over again, you're presented with both the .quote and .description divs showing at the same time (see example screenshot here).
I thought I was preventing them from firing at the same time based on how my functions are set up, but I must be missing something important for this to be happening.
Here is my fiddle so you can see it in action.
Could somebody please lead me in the right direction?

My guess would be to also clear the animation queue for the quote element on hover.
$(this).find(quote).clearQueue().stop(true, true).hide();
I've updated the fiddle accordingly .

Related

Mouse movement blocks 'transitionend'

Introduction
I'm using Semantic-UI's sidebar functionality, which gives you a button that triggers a sidebar that pushes the content from the left (in this case).
I want to unfold that same sidebar by hovering with the mouse on the left side. I realize there are several ways to do it (as these often do. Maybe just checking the X position of the mouse would work but that's beside the point); I chose to create a transparent div on the left side and make its :hover pseudo-class to trigger the sidebar:
// create sidebar and attach to menu open
$('.ui.sidebar').sidebar('attach events', '.toc.item');
// hover transparent div to trigger the sidebar too:
$('.sidebar-trigger').hover(function() {
$('.ui.sidebar').sidebar('show')
});
// hide() and show() the sidebar accordingly to use the sidebar:
$('.ui.sidebar').sidebar('setting', {
onShow: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
Problem
Now, it all works except for one occasion: when you don't stop moving the mouse as the sidebar opens. I've looked at $(document).on('transitionend', function(event) { ... } and that mouse effectively prevents the transition to finish.
Resources
I've put a blue background on my .sidebar-trigger and made a small video/gif so as to be clearer.
I moved the mouse like a crazy creature but with natural gestures the problem occurs as well.
I'm using Semantic-UI's guide on this thing: http://semantic-ui.com/modules/sidebar.html#/settings (I've also tried onVisible and onHide with no luck)
This is a OSX Yosemite 10.10.3 running Chrome 45.0.2454.101 (64-bit)
jsfiddle with the problem at hand
PS: It seems it might be an OSX Chrome bug?
I would try using one and mouseover:
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
Then, when it has finished animating, reattach the event:
$(document).on('transitionend', function(event) {
$('.sidebar-trigger').one('mouseover', function() {
$('.ui.sidebar').sidebar('show')
});
});
I think what is happening is that the hover event is getting called multiple times - every time the element is hovered, then goes over a child element, and then goes back over the hover element, and things are getting mixed up at some point. So you need to only call show if it's not already shown.
Here is a working example: Fiddle
I believe when the element was hovered, it was adding a classes 'uncover' and 'visible', and another called 'animating' which wouldn't fire until the mouse stopped moving. I changed the jQuery slightly to only add classes 'uncover' and 'visible', and it still animated okay. However, the body was pushing right too far by 175px, so I had to edit the class that was causing that (noted below) from 260px to 85px. This DOES get the menu acting properly though from my understanding.
$('.sidebar-trigger').mouseenter(function() {
$('.ui.sidebar').addClass('uncover, visible');
$('body').addClass('mleft175');
});
$('body').click(function() {
$('.ui.sidebar').removeClass('uncover, visible');
$('body').removeClass('mleft175');
});
and then add overriding class
.ui.visible.left.sidebar ~ .pusher
{
-webkit-transform: translate3d(85px, 0, 0);
transform: translate3d(85px, 0, 0);
}
Right now it is set to hide the menu when the body is clicked. Alternatively you can hide it when the mouse leaves the sidebar menu:
$('.ui.sidebar').mouseleave(function(){
$(this).removeClass('uncover, visible')
});
Ok, my first answer was (of course) way too much work for what it really needed. The onVisible seems to work perfectly. Was that not working for you? Demo HERE
Simply change 'onShow' to 'onVisible' in your sidebar setting:
$('.ui.sidebar').sidebar('setting', {
onVisible: function() {
$('.sidebar-trigger').hide();
},
onHidden: function() {
$('.sidebar-trigger').show();
}
});
As shown on the Semantic UI site, the onVisible fires when the animating starts. The onShow fires when the animating finishes. So what you were doing was hiding that blue / transparent bar when the animation was finally done (the .animating class noted in my previous answer), as opposed to when it starts. If you need further explanation please let me know.

strange behaviour while sliding images on carousel

I made a jsfiddle so you can reproduce the bug:
FIDDLE
I implemented a carousel to display 3 images. There's a current image (the image being displayed) and the other two remain hidden until I click one of the lateral arrows, causing the next image to slide from the side overlaying the (now previous) current image.
I've been 2 hours trying to figure out why there are certain specific 'transitions' in which the animation doesn't seem to work. For example, when clicking the left arrow to pass from the first image to the second and from the second to the third the animation works fine, but when clicking it again, the transition from 3 to 1 doesn't perform the slide animation. When moving in the opposite direction (using the right arrow) only one transition is animated. I think the problem has to do with that if in the click event handler function, but couldn't spot what's causing it.
Any help would be greatly appreciated.
TIA
The underlying issue here is related to the z-order of the three images. Your slide animations are only showing up where the image being slid in is above the displayed image; the "broken" transitions are actually occurring, they're just obscured by the "higher" visible image.
You can fix this by explicitly setting the z-index of the new and current image. For example, on the right transition:
prevLandscape.zIndex(1);
currLandscape.zIndex(0);
If you do this, you'll also need to increase the z-index of the arrows so they're above the images.
Fiddle
jsfiddle
The issue is with the hide method you just simply hide it add the slide transition for the hide method.
change this line currLandscape.hide(); to currLandscape.hide("slide");
there seemed to be a problem with the order of the images also. please try this code out. The code is reuse of the previous image arrow code. Just try it out.
$('.arrowRight').on('click',function(e) {
var currLandscape = $(this).siblings(".currImg");
var nextLandscape = currLandscape.nextAll(".hiddenImg").first();
var currDesc= $(".currDesc");
var nextDesc= currDesc.nextAll(".hiddenDesc").first();
if (nextLandscape.length == 0) {
nextLandscape = currLandscape.siblings('.hiddenImg').first();
}
if (nextDesc.length == 0) {
nextDesc= currDesc.siblings('.hiddenDesc').first();
}
nextLandscape.show("slide", { direction: "right" }, 400, function() {
currLandscape.hide("slide");
});
currDesc.fadeOut().removeClass('currDesc').addClass('hiddenDesc');
nextDesc.fadeIn().removeClass('hiddenDesc').addClass('currDesc');
currLandscape.removeClass('currImg').addClass('hiddenImg');
nextLandscape.removeClass('hiddenImg').addClass('currImg');
});

Issue with jQuery .fadeIn() queue

I'm having a strange issue with jQuery and a fading loop. Let's see a bit of code (here on JSFiddle: http://jsfiddle.net/4fcvze4m/1/):
$('.spin-block').each(function(index) {
$(this).delay(index * 200 + 800).fadeIn();
})
.spin-block {
display: none;
margin: 5px;
padding: 10px;
background: #a00000;
color: #fff;
}
<div class="spin-block">1</div>
<div class="spin-block">2</div>
<div class="spin-block">3</div>
<div class="spin-block">4</div>
<div class="spin-block">5</div>
<div class="spin-block">6</div>
<div class="spin-block">7</div>
<div class="spin-block">8</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
That's basically it.
So, sometimes (it's very random), one of this block don't show up, Mostly 5th or 6th. It can work perfectly 30 times a row and have this issue on the next refresh. I tried a workaround with this :
$('.spin-block')
.each(function(index) {
$(this).delay(index * 200 + 800).fadeIn();
})
.promise().done(function() {
console.log('debug');
$(this).fadeIn();
});
When everything is ok, the 'debug' log show off, but when there's one missing, the console remain empty, even if next .spin-block appears. And if i force some blocks not to show, then this workaround nicely works and fade in the remaining ones (after the end of the animation, but i don't care) :
var i = 800;
$('.spin-block')
.each(function() {
i += 200;
if (i > 1400) {
$(this).delay(i).fadeIn();
}
})
.promise().done(function() {
console.log('debug');
$(this).fadeIn();
});
There it is. Have you any idea of what's going on ? It's totally random, on any browser as far as i tested. Thank you!
Please note: This is not happening on the Fiddle I posted. I know it comes from my own JS file, but I don't have the possibility to put everything here. Besides, this is pretty much the first thing that happen in my init function.
I believe, the issue you are facing is due to the class of Divs being altered dynamically during run-time and the asynchronous nature of how jQuery animations work.
This JSFiddle explains the same. Essentially, it is the same set of Divs you had created initially. But instead of just animating them (iterating through $.each), I am introducing one more statement after the $.each which will remove the class associated with one of the Divs. See code below:
$(function () {
$('.spin-block').each(function (index) {
$(this).delay(index * 200 + 800).fadeIn(1000, fnCallback(index));
});
//Removing class dynamically for div 5 alone.
$('.spin-block').eq(4).removeClass('spin-block');
});
You would expect this to animate all Divs and then removeClass from the 5th Div. But what really happens is, animation starts, simultaneously Class "spin-block" is removed from Div 5, thus blocking Div5 from being animated!

Conflicting click and hover events on the same element

I have a refresh "button" (actually a png image) which the user can hover their mouse over, turning it from gray to blue. When refresh is clicked, the image changes to a play "button" which exhibits similar color-changing behavior, except when you click on the play image it should switch back to the original refresh image.
The problem is that, after clicking on the refresh image, when I click on the play image without removing my mouse from the image, it doesn't change back to the refresh image.
I have already looked into event propagation stopping.
Here is my code:
$('#refresh').click(function (event) {
if (!tMinusZero) {
$('#refresh').html("<img src='play_small_hover.png'>");
tMinusZero = true;
event.stopImmediatePropagation();
} else {
$('#refresh').html("<img src='refresh_small_hover.png'>");
tMinusZero = false;(
event.stopImmediatePropagation();
}
});
$('#refresh').hover(function () {
if (!tMinusZero) {
$('#refresh').html("<img src='refresh_small_hover.png'>");
} else {
$('#refresh').html("<img src='play_small_hover.png'>");
}
}, function () {
if (!tMinusZero) {
$('#refresh').html("<img src='refresh_small.png'>");
} else {
$('#refresh').html("<img src='play_small.png'>");
}
});
Some interesting things I have noticed whilst trying to debug:
If I move my mouse through #refresh too fast the hover will 'stick' i.e. the implicit mouseleave won't fire.
Commenting out the hover code fixes the clicking problem.
Leaving the hover code in, if I click outside of the image but inside of the div, without removing my mouse from the div, fixes the clicking problem.
Removing my mouse from the div before trying to click again fixes the clicking problem.
After clicking on the image, the image it changes to will flicker between the hover and non-hover image if I move my mouse over it, but not if I first remove my mouse from the div.
When I experience the clicking problem, the offending image will flicker momentarily, as if switching to one of the non-hover images, and then quickly changing back to the offending image.
It seems to me that my two event handlers have conflicting interests, but stopping the event propagation doesn't seem to help.
Why don't you try to tackle your problem with CSS, i think it will be more elegant, make a small DIV, with a background corresponding to your image, define a hover state and an active state, plus a small script to change between 2 more additional states
Something like:
CSS:
#refresh[data-state=notclicked]
{
background-image:url('play_small.png');
cursor:pointer;
}
#refresh[data-state=notclicked]:hover
{
background-image:url('play_small_hover.png');
cursor:pointer;
}
#refresh[data-state=clicked]
{
background-image:url('refresh_small.png');
cursor:pointer;
}
#refresh[data-state=clicked]:hover
{
background-image:url('refresh_small_hover.png');
cursor:pointer;
}
Of course you will have to define the width and the height in the CSS, to a fixed width.height which matches your png size.
Than the js:
$("#refresh").click(function(){
if ($(this).attr('data-state')=='clicked') {$(this).attr('data-state','notclicked');}
else {$(this).attr('data-state','clicked');}
});
If I understand correctly how you want it to behave, your code seems to work just fine, even though there's a typo in the click event (round bracket).
tMinusZero = false;(
Also, just for improve the code you can replace
$('#refresh')
with
$(this)
inside the event as it'll refer to the dom element you attached the event to.

how to remove hover on click

I'm working on a jQuery game. I have a 4 divs in a 2x2 design. The player needs to pick 1 option and verify with another button. The thing is, I have a hover effect adding a class which changes the background with a low opacity, and a click effect setting the background with a higher opacity. For divs 2, 3 and 4 it works fine - I hover and background changes color with opacity 0.3 and when I move the mouse out, it goes back to white. And when I click it, it changes the background to 0.4 and the hover doesn't affect them anymore. However, this is not working for the first div: the div changes background color on hover, but when I click it ,it keeps the hover color, and when I mouse out I see the click color, and every time I hover it changes the hover color again and so on.
Why is it happening only on div 1?
Code:
//hover effects
$(".respuesta1,.respuesta2,.respuesta3,.respuesta4").hover(
function () {
$(this).addClass("respuestahover");
},
function () {
$(this).removeClass("respuestahover");
});
//on click function for div1
$(".respuesta1").on("click", function () {
//if it hasnt been clicked, toogle class and change var to true
if (prendido1 == false) {
$(this).toggleClass("respuesta1b");
prendido1 = true;
//if any of the other divs are clicked by the time you are clicking unclicked 1, turn them off
if (prendido2 == true) {
$(".respuesta2").toggleClass("respuesta2b");
prendido2 = false;
}
if (prendido3 == true) {
$(".respuesta3").toggleClass("respuesta3b");
prendido3 = false;
}
if (prendido4 == true) {
$(".respuesta4").toggleClass("respuesta4b");
prendido4 = false;
}
//if is already clicked, turn off and change var to false
} else {
$(this).toggleClass("respuesta1b");
prendido1 = false;
}
});
The last part is repeated for every div "respuesta2", "respuesta3", etc..
Any idea?
EDIT
I was trying to clean up the code to make a jsFiddle and I think I got it to work:
http://jsfiddle.net/bqySN/2/
I'll just leave the code there if anyone is interested, be aware the code is unpolished and it need more generalisations.
EDIT 2
After some testing I actually found the problem:
if I alter the order of my css clases the app goes crazy:
This one is correct, with hover first
.respuestahover{
background-color:#f00;
opacity:0.2;
}
.respuestab{
background-color:#f00;
opacity:0.5;
}
This one is incorrect, hover second:
.respuestab{
background-color:#f00;
opacity:0.5;
}
.respuestahover{
background-color:#f00;
opacity:0.2;
}
I'm not really sure why it is behaving like that, but I'm glad I figure it out.
You are adding a class on hover... why would you do that via javascript if you can just use the :hover state from css? For example:
#foo .element p { color: red; }
#foo .element:hover p { color: blue; }
EDIT:
Sorry, I miss the original question.
If you want to remove the hover effect after clicking, you have lot of different ways to do this. You can remove the class defined with the hover via css, or if you want a jQuery solution you can use mouseenter/mouseleave with .on and then unbind with off.
See the following fiddle example.
You should simplify the bindings to just target them a little more generically, then remove the hover classes on all of them:
$(".respuesta").on("click", function (index) {
$(this).removeClass("hover");
// do other things
});
You can also use the index to find which number they are if they're in a list.
if you want the hover to not override the click, give the click an active class and tell the hovers to work on everything but them:
$('.respuesta:not(.active)').hover(function() {
// do something
}

Categories