JQuery: Reading offset().top & .height() after div .animation() & .slideDown() done

Hi all,

I have the below code to calculate how tall a 3rd div needs to be. This code works except for one flaw. It always seems to read the previous .top and .height() values.

As .annoucement-area-880 and .content-annoucement-bottom-880 are dynamic (two different types of accordions) they resize depending on what element gets clicked.

One uses the .animate() method while the other .slideDown().

I tried enclosing the below within a $(document).ready(function(){…}); but this doesn’t help.

In conclusion, below code works fine except it reports previous (aka old) values which in effect cause it to calculate an currently invalid height.

Any ideas anyone? Thanks.

var div1 = $( ".annoucement-area-880.id1" );	
var div1_offset = div1.offset();	
var top = div1_offset.top;
			
var div2 = $( ".content-annoucement-bottom-880.id2" );			
var div2_offset = div2 .offset();	
var bottom = (div2_offset .top + div2.height());
			
var height = (bottom - top);

From what I gather I should append a function to the animation as a callback once the animation finishes. My animation looks as follows:

$el.animate({
"font-size": "18px",				
paddingTop: 10,
paddingRight: 5,
paddingBottom: 5,
paddingLeft: 10
}).next().slideDown();

If I append a callback as below then it fails to show the accordion meaning there’s some kind of syntax error normally (or so I also gather). I think the .next().slideDown() is interfering somehow. I’m also not too happy about inserting a animation duration - is this optional?

$el.animate({
"font-size": "18px",				
paddingTop: 10,
paddingRight: 5,
paddingBottom: 5,
paddingLeft: 10
}).next().slideDown(), 1234, function() {

Run necessary code here (i.e. inline code or call an external function from here)

});

You have to make sure you do this measuring after the animations are done. You can wrap this all up in a function and supply it to animate() as a callback.

[edit]Yes, you have a syntax error there. You’re not supplying the function to animate(). As you can see, it’s not within the brackets for animate(). You can keep the defaults and just supply your callback using the second syntax type, using an object for the options:

$el.animate({
  "font-size": "18px",                
  paddingTop: 10,
  paddingRight: 5,
  paddingBottom: 5,
  paddingLeft: 10
},
{
  complete: function() {
    // measurement stuff here
  }
).next().slideDown();

By the way, it looks like you’re using “id2” and “id1” as class names. That’s pretty silly.[/edit]

Thanks for your insight.

Yes, the more I read on this topic the more it seems to be the case.

Unfortunately as I wrote above, I’m having problems getting the syntax right, most likely because there’s a .next().slideDown() in there.

I was also thinking I could enclose the entire animation within its own delay routine thus stopping further execution prior to finishing the animation(s).

Looks like we’re just missing each other. I’m assuming you haven’t seen my edit. :slight_smile:

Thanks a lot, that worked nicely.

The class names (i.e. ID1, ID2) are just examples to make it easier on the eyes for a wider audience, their names are completely different in the code.

What would you do if the accordion in question has several animations and ideally you would want to wait for all of them to finish before calling a measurement function (or running inline measurement code)?

Calling the same measurement function X times seems a waste of resources.

I’m thinking that each time an animation finishes a variable could be set to 1 signally true (aka finished). Then at the bottom of the code there would be a while loop which would sit there running until all animation variables = 1.

Any other solution?

Why don’t you just put the measurement stuff in the callback for the last animation? Is there a way to identify the last animation (e.g. the last in an array of elements)?

Using a while loop works too. If you know how many animations are going to run, you just increment the variable with each animation that runs. Then when they’re all done, the while loop’s final action is to run the measurement stuff.

The measurement stuff is currently in the last animation but it seems animations aren’t finishing in the order they get triggered. I see a 1-2px height discrepancy which logically pinpoints that at least one animation is still running while the last animation is already calling the callback.

I’m going to try the while loop variable incrementation method.

Thanks again.

Hmm, 1-2px isn’t a lot. Check if that’s the case by using a timeout:

$el.animate({
  "font-size": "18px",               
  paddingTop: 10,
  paddingRight: 5,
  paddingBottom: 5,
  paddingLeft: 10
},
{
  complete: function() {
    window.setTimeout(function() {
      // measurement stuff
    }, 1000); // 1s delay just to make sure
  }
).next().slideDown();

I’ll look into this once I get the main code path working. I get the odd feeling that the 1-2px shows up in Chrome but not Firefox.

Currently battling my mind over how to synchronise two accordion animations, one loading up above the other but both influencing how a side div gets setup (its height).

Indeed, window.setTimeout does help and as far as my eyes can tell and removes the 1-2px bug too. It also stops the side div from getting a misinterpreted height (without the delay it sometimes spot on, sometimes too short, and at other times slightly too long).

What I found is that some browsers (i.e. Chrome) only require 50ms but others such as Firefox require 350ms to be safe (get 100% results each and every time).

It’s a pity there isn’t some kind of more refined synchronisation mechanism whereby animations in the same function appearing in the code sequentially (though executed adhoc it seems by JQuery) can be grouped into a single watch which won’t allow further execution (or even perhaps given them priority execution) until they finish.

In Windows (Win32) I remember there being critical states and semaphores which meant you had exclusiveness for what you were doing (i.e. a locked state).

Let me get this right: you have multiple animations running at once and you can’t tell which one is going to finish first? Then the method of increasing a counter when each finishes should work flawlessly. That is, unless the problem is somewhere in jQuery’s calling of the callback function, but I doubt that.

It turns out that doing it the variable++ way isn’t as straightforward as one would originally believe.

I gave it a try and in IE8 I got a popup saying such and such a script is hogging resources and may slowdown the processing of this website, do you want to let the script continue to run.

Having that popup in front of users isn’t going to go down well.

IE8 is very slow at animation code which must be partly to blame for its ill desire to misinterpret the JQuery code as slow and generally slow rendering engine.

The other part of it is that one of the accordions is both a horizontal and vertical accordion in one and any one click (init) causes 32 animations.

If there’s no While loop waiting for all animations to complete IE8 just does as its told.

Given this I’ve currently used the window.setTimeout() and set it to 350ms. This is enough for stable (aka synchro) operation in Chrome, Firefox, IE7, IE8, Safari and Opera but not Flock. In Flock it seems to need more time as otherwise it will misinterpret the values needed to calculate the height of the side div.

350ms as I say works fine for 90% cases (all except Flock) but then again it may not be enough if someone has a slower computer (likely) and thus the page renders slower regardless of browser. I could use 1000ms as you originally suggested but then each click on the bottom accordion (multi-dimensional) causes 1 second pause before the right side div adjusts its hight to match the far bottom offset of the accordion’s container div.

The variable++ method would be great if it wasn’t got the risk of a “script running slow” warning giving a user headaches (especially if unwarranted). As far as I can see it has to be in a While or Do loop and If statement checking is variable = all animations complete, in which case a break; out from the loop and call to the measurement function.

P.S. Naturally modern engines such as in Chrome, Safari run the website silky smooth so it’s not slow code, it’s merely IE being too sensitive.