Unexpected delays in jquery animations - javascript

I have a list-element that is initially set to max-height: 200px; overflow-y:hidden;. Beneath that element there is a h3-element you can click to show the entire list. For that purpose, I animated the maxHeight-property of the element with the jQuery animate() method. The animation should last 1.2 seconds. After that, the text below the list changes from "show more" to "show less". When you click on it, this animation is reversed, again with a duration of 1.2 seconds, after which the text changes back to "show more".
Everything works fine, except for some unexpected delays: After the first animation which raises the maxHeight-property of the list, it takes another second or so for the text to change as well. Then, if you click the "show less" text, nothing happens for about a seconds before the animation starts.
What is causing these unexpected delays?
JS Fiddle
Edit: As Wa Kei explained, this delay is caused by the "excess" height, since the element is actually less than 1200px high. So how could I rework my function so that it works as intended while not having to use a fixed height value?
I can't use handy jQuery methods like toggle(), show() or hide() because all of those hide an object entirely instead of accepting a minimum and maximum height or something like that.
I'm open to different solutions with JS / JQuery / CSS, but the html structure should remain unchanged.

Here is an example for the first part:
if (!list.hasClass('opened')) {
list.animate({
maxHeight: '1200px'
}, {
duration: 1200,
progress: function(){
if(list.css('maxHeight') > list.css('height')) {
$(this).stop();
}
},
always: function() {
$('#show_more').text('(weniger anzeigen)');
list.addClass('opened');
}
});
}
Edit:
I reworked your code a bit: http://jsfiddle.net/z83cpbvj/1/
#progress (jQuery API) A function to be called after each step of the animation, only once per animated element regardless of the number of animated properties.
#always (jQuery API) A function to be called when the animation completes or stops without completing

Related

Javascript focusing a large div with fixed header

I'm using a tiny library called '$.scrollTo' to animate a scroll to a div element in my html. at the top of my page I have a fixed navgation bar.
at the end of the animation, I would like to have that div focused (for accessibility). if my div is to large, at the end of the animation, the fact that it gets focus - simply sends it a bit off the screen.
This does not happen with small divs.
here is my code (check jsfiddle below):
$('#buttonid').on("click", function() {
//fixed nav bar height (to compensate when scrolling)
var fixed_navbar_height = $("#navbar-id").height();
//the element to scroll to
var $go_to_selector = $("#topic2");
$.scrollTo($go_to_selector, {
duration: 1000,
offset: -fixed_navbar_height,
onAfter: function() {
//if you comment out this .focus it works as intended.
$go_to_selector.focus();
}
});
});
here is a JSFIDDLE example:
https://jsfiddle.net/dy35obpq/3/
obviously the onAfter messes it up, but i would like both the animation and the focus. Any ideas on how to implement a focus on a large div without letting it change the scroll bar ? suggestions are more than welcome.
Try this.
onAfter: function() {
$go_to_selector.focus();
$(window).scrollTop($($go_to_selector).offset().top - fixed_navbar_height);
}
I have simply added this line in your onAfter callback.
$(window).scrollTop($($go_to_selector).offset().top - fixed_navbar_height);
and it seems to have fixed the problem while still retaining focus. You might want to use css to disable the focus blue highlight.

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.

Can't get hidden image to display with .show()

I'm using the vimeo api to slide a video off the screen after it finishes playing. Underneath the video player, hidden, I have an image that says 'replay'. The image though is slightly bigger than the player so I want to hide the image via .hide() or display: none in the css and then show it after the animation of the video player completes.
Here's my js:
$(document).ready(function() {
$(".vimeo-container img").hide();
$('iframe.vimeo').each(function(){
Froogaloop(this).addEvent('ready', ready);
});
function ready(playerID){
Froogaloop(playerID).addEvent('finish', onFinish);
}
function onFinish(playerID) {
var player = "#" + playerID;
$(player).animate({width: "0%"}, 750, function() {
$(player).next("img").show();
});
}
});
So the first line is hiding the image. And then when the onFinish function completes I'm trying to show() the image, but it won't work. I should note that when I reverse it and do:
$(player).next("img").hide();
it works.
Here's my HTML:
%section#container1
.row.video-left
.large-8.columns
.vimeo-container
.flex-video.widescreen.vimeo
%iframe.vimeo#player1{allowfullscreen: "", frameborder: "0", height: "225", mozallowfullscreen: "", src: "http://player.vimeo.com/video/60122989?api=1&player_id=player1", webkitallowfullscreen: "", width: "400"}
= image_tag "behind1.png", class: "behind1"
And CSS:
.vimeo-container {
position: relative;
.behind1 {
margin-top: -27em;
}
I've also tried setting display: none in the css, but that wont work either. Not sure what I'm missing.
Thanks.
EDIT
function onFinish(playerID) {
var player = "#" + playerID;
$(player).animate({width: "0%"}, 750, function() {
console.log($(player));
$(player).next().show();
});
}
When I log out ($(player) it returns:
And when I log out console.log($(player).next()); it logs out the image that I am trying to show.
According to the jQuery documentation on the animate method here:
Note: Unlike shorthand animation methods such as .slideDown() and .fadeIn(), the .animate() method does not make hidden elements visible as part of the effect. For example, given $( "someElement" ).hide().animate({height: "20px"}, 500), the animation will run, but the element will remain hidden.
I had a similar need in a project and what worked for me there was to set the z-index of the element I wanted to hide to be less than that of the background. Then, when I wanted to show (or, in your case, animate) I could apply the jQuery methods to the element as if they were hidden (by increasing the z-index so that the element becomes visible), yet not incur the undefined behaviour of attempting to manipulate a hidden element.
Another option would be to move the element off the screen by way of a negative (x, y) coordinate and work from there. I'm not sure which visually would be more appealing in your use case but mention it for completeness.

jQueryui animation with inital undefined height

See the following fiddle:
[edit: updated fiddle => http://jsfiddle.net/NYZf8/5/ ]
http://jsfiddle.net/NYZf8/1/ (view in different screen sizes, so that ideally the image fits inside the %-width layouted div)
The image should start the animation from the position where it correctly appears after the animation is done.
I don't understand why the first call to setMargin() sets a negative margin even though the logged height for container div and img are the very same ones, that after the jqueryui show() call set the image where I would want it (from the start on). My guess is that somehow the image height is 0/undefined after all, even though it logs fine :?
js:
console.log('img: ' + $('img').height());
console.log('div: ' + $('div').height());
$('img').show('blind', 1500, setMargin);
function setMargin() {
var marginTop =
( $('img').closest('div').height() - $('img').height() ) / 2;
console.log('marginTop: ' + marginTop);
$('img').css('marginTop', marginTop + 'px');
}
setMargin();
Interesting problem...after playing around with your code for a while (latest update), I saw that the blind animation was not actually firing in my browser (I'm testing on Chrome, and maybe it was firing but I wasn't seeing it as the image was never hidden in the first place), so I tried moving it inside the binded load function:
$('img').bind('load', function() {
...
$(this).show('blind', 500);
});
Now that it was animating, it seemed to 'snap' or 'jump' after the animation was complete, and also seemed to appear with an incorrect margin. This smacks of jQuery not being able to correctly calculate the dimensions of something that hadn't been displayed on the screen yet. On top of that, blind seems to need more explicit dimensions to operate correctly. So therein lies the problem: how to calculate elements' rendered dimensions before they've actually appeared on the screen?
One way to do this is to fade in the element whose dimensions you're trying to calculate very slightly - not enough to see yet - do some calculations, then hide it again and prep it for the appearance animation. You can achieve this with jQuery using the fadeTo function:
$('img').bind('load', function() {
$(this).fadeTo(0, 0.01, function() {
// do calculations...
}
}
You would need to work out dimensions, apply them with the css() function, blind the image in and then reset the image styles back to their original states, all thanks to a blind animation that needs these dimensions explicitly. I would also recommend using classes in the css to help you manage things a little better. Here's a detailed working example: jsfiddle working example
Not the most elegant way of doing things, but it's a start. There are a lot more easier ways to achieve seemingly better results, and I guess I just want to know why you're looking to do image blinds and explicit alignment this way? It's just a lot more challenging achieving it with the code you used...anyways, hope this helps! :)

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