How to troubleshoot a bad jQuery animation - javascript

Recently, I was writing code for a website template that required some element animation. Naturally, I decided to get my jQuery on and use some of its standard libraries to animate my div. In this case, I wanted to use the slideUp() function. When I coded it up and ran a trial, I noticed that the animation was very choppy, so I decided to look for clues here and on the internet and came up fairly short.
The question Div slideUp and slideDown animation choppy with inline-block display provided some hints, but nothing that I would end up actually using. So I am wondering: how have you dealt with this, per scenario, in the past? I will be posting an answer with the solutions I found to be helpful.

The Terrible min-height/min-width
The first and most common bug I have run into is the use of min-height or min-width. As noted here, it will cause the animation to be jumpy regardless of your standard height setting. Why does this happen?
Let's say you have a div that has a height of 75 pixels and a min-height of 50 pixels, and you wish to use some animation function that reduces the height of that div to zero (such as slideUp()) When the animation starts, it will begin to subtract from the total known height. When it reaches the set min-height, it will attempt to set the div height lower, but will not be able to and stick there until the display:none is added to the element's style, causing that presumed "choppy animation". It's not actually choppy: it's failing. If you were to run a standard animate() such as $("div").animate({ height:'0px' )}) on a div with a min height set (greater than 0), you would find that the animation simply sticks at the set min-height.
The solution to this problem is to simply unset any min-height for the div in css or to set min-height: 0; if the min-height is declared elsewhere.
The unfortunate no height/width
Another common bug is to simply set no height (or width, depending on the direction of the animation) on the object at all. This can cause the animation function to jump because of an incorrectly calculated height that it needs to adjust for. An unset width/height can have varying results and might succeed or fail depending on the DOM around and contained in it. It is best practice to set a height and width on any element one wants to animate. Percentage heights/widths are fine.
Padding/Margin Bloody Padding/Margin
You might run into a problem with choppy animations caused by collapsing padding/margins of the element you are attempting to animate, or any elements inside it. This is a simple problem fixed by adding a border: 1px solid transparent; to the element you are animating. This creates a bounding for jQuery to use so that it essentially ignores the collapsing padding/margins.

Related

How does jQuery Slidedown get final height of hidden item before showing it?

I'm trying to replicate jQuery slideDown() in GSAP and I'm having trouble working out how jQuery calculates the height of an item which is currently hidden as if it was set to height:auto.
I've tried trawling the code on GitHub but can't find any code which seems to be doing this in jQuery.fn.slideDown or jQuery.fn.animate which it calls.
There are several similar questions on SO and several solutions proposed, all of which seem to have their own problems:
Clone the element, position it off screen and calculate its height. This won't work if the element or any of its child elements have a height set by CSS styles which require the element to be in its original place in the DOM (e.g. an .accordianItem might only be styled if it's inside its .accordian).
Display the item, remove height:0 and quickly calculate the height before hiding the element again and then stating the animation. This might flash the content quickly while calculating the height.
Use visibility:true to show it in place while calculating the height. This would stop the flash and still keep the element in the same position in the DOM for correct height calculation, but it would still push other items below it down because visibility:false items still have a height.
Calculate the height of an item before it's hidden and store it in a data attribute so we know it when we want to open the item later. This won't work if any dynamic content changes the height of the item whilst it's hidden.
jQuery slideDown() "just works" every time so I'd be really interested to know how it works, but I just can't work out where it's doing this. I'm also surprised that GSAP can't do this out of the box, or that nobody has shared a proper solution to this before.
Any help would really be appreciated.
It turns out that if you use $.height() to get the height of an element with display:none it doesn't return 0 as you would expect, it actually sets visibility:hidden, position:absolute etc. and sets display to block to give you the correct height back. I assume this is what's being used internally when doing a slidedown.
This answer helped me a lot.
jQuery: height()/width() and "display:none"
Just to be clear about how this seems to avoid all the problems in my original question. It's basically doing number (3) but avoiding the problem of pushing lower content down the page because it's also set to position:absolute while the height is being calculated. A very simple elegant solution

Do you really need a width on floated element?

Say you have a Div (id="toolbar"), and inside that toolbar you have a Div (id="ButtonHolder") that contains 2 buttons. If you float the #ButtonHolder to the right and don't set an explicit width on it, is that kosher?
I've read on stack overflow that you should always set a width on a floated element. The buttons text might change, from save to apply, and I don't want to have to adjust #ButtonHolder's width every time.
I thought about setting #ButtonHolder's width to auto, but the browser does that by default so it seems unnecessary to set it's width to auto. I'm worried the browser might not always float #ButtonHolder the way I think it should.
A change from "save" to "apply" isn't going to take up much more room, to be honest.
In principle, yes you should always set a width - if you don't, then say you have the button float:left; and another <div> float:right;. If you don't set widths, they're not going to take up the full screen width, so any elements you put in below are going to try to position themselves in the gap between the two.
It is also a good idea to have a 100% width container element for this particular scenario to prevent the described effects.
float and position usually come with a cost. You should try to first find other ways to position elements within your layout. You can should consider other options such as margin, padding, display: inline-block;, text-align ... etc.
I would recommend reading this.
To answer your question directly. Setting width for floats is not written on stone but not doing so, usually means trouble later. At least in my personal point of view.

Issue with container holding marquee animation - jquery

I’m having an issue with a container holding the marquee and i’m not sure what’s causing it.
Essentially, the container is stretching way too far, causing the animation to flash across extremely fast (because the animation accounts for the width).
I don’t want to state a width for the marquee because I want the container to stretch to whatever its siblings width is.
I’ve created a fiddle to display what’s happening. In the fiddle, i’ve included the exact html included on my own webpage.
I assume there’s an issue with the css of one of the other elements, but what? What's causing the container to stretch to extreme lengths?
FIDDLE: http://jsfiddle.net/uz9pG/
This is the jquery plugin marquee that i'm using http://jquery.aamirafridi.com/jquerymarquee/
Tables, fluid widths and overflow hidden tricks don't really get along well. You'll either need to change your code to use a different markup structure or put a fixed width on one the containing divs within your <td>.
Also, you have conflicting settings in your JS vs data-attributes in your markup. <div data-duration="2000" data-direction="right" class="marquee">
Here's a working version. http://jsfiddle.net/uz9pG/2/ Takes a second for the marquee to start. You'll need to adjust your margin code to sort that out. This one removes the tables altogether but you can just as easily add a fixed width to something like your .module_content div if that works for your design.
.module_content {
width: 400px;
overflow: hidden;
}

Transform causes div to go away

I am creating a 3d carousel with CSS3 and jQuery. This is my code.
Currently, my problem is that if I set this line of css code to main class, the location, and the zoom of everything will be changed:
-webkit-transform: rotateY(20deg) ;
My question is, how to rotate the main class without any other changes?
The problem is your -1000px margin on .main. If you get rid of it, you can start to apply rotations to that DOM element.
http://jsfiddle.net/upEC6/53/
Try using negative degrees to rotate.
This is a really interesting issue. I'm using Google Chrome and have not tested in Safari, but it appears that at least Chrome seems to create a new painting stack when -webkit-transform is used. If you notice, <div class="main"> no longer has a height. It's as though the contents of the element have been abstracted from their parent.
In any case, I noticed that it becomes visible again if you give it a static height instead of a percentage (like 500px) and if you remove the negative margin (which doesn't appear to do anything anyway).

Most minimal solution to enlarge child so as to create scrollable space

In html if a child div is bigger than the parent div it will create scrollbars on the parent div if you set the appropriate style rules.
However, I want it so that when an attempt to scroll occurs (by hitting the arrow keys, making the appropriate javascript call on the element) the minimum needed expansion in size occurs on the child occurs such that it can scroll to the degree that it would have scrolled anyway if the child was already that big.
To state that again: if the child was 300px width within a parent of 200px width, and I hit right arrow key, and it scrolls 20 pixels to the right, then if the child is 200px in that same scenario, I want it to enlarge in width by 20 pixels and no more if possible, and then scroll 20 pixels to the right.
This is all assuming there is no way to make a sub-element scroll within its parents regardless of whether it's actually larger than its parent. There might well be so I apologise in advance if i haven't done enough research. :)
You probably know that the style overflow: scroll will make scroll bars show up regardless of child size. Do you actually need Javascript to boost the child dimensions, rather than having an extra wrapper div with greater dimensions that would cause the scroll like in this demo? I know Safari already scrolls approximately 20px on arrow key down within selected scroll divs by default, and I would assume other browsers have this functionality as well.
If you do have need for increasing the div size with javascript, jQuery has a few functions that would be helpful. The .keydown() method looking for left and right arrow keys (which I believe are key codes 37 and 39 respectively) and the .animate() or some other CSS method would work to resize the div chained together.
The .scroll() method could come in useful as well. You could chain the resize code to the scroll method with an overflow: scroll property already applied. I would test to see if browsers will trigger the .scroll method even if the scroll bars are empty. If not, you could potentially make the child only 1px wider/taller then the parent div and then rely on the jQuery to further resize on the user's scroll.
Broadly speaking, I would advise against the javascript/jQuery resize and scroll. Compatibility with different browsers, especially mobile browsers, would be inconsistent or unusable. I don't know exactly what your needs are, but if it can be accomplished with only HTML/CSS it would be much cleaner and more compatible. I would reserve the javascript for cases where usability will not be lost if it does not run.

Categories