views:

473

answers:

7

HTML Code

<div id="foo">
    <h1>foo</h1>
    <p>Pellentesque habitant morbi tristique.</p>
</div>
<div id="bar">
    <h1>bar</h1>
</div>

jQuery Code

$('#bar').click(function () {
    $('#foo p').hide('slow').appendTo('#bar').show('slow');
})

Expected Result

When #bar is clicked

  1. hide the p element in #foo
  2. append p to #bar
  3. show p which is now a child of #bar

Actual Result

  1. append p to #bar
  2. hide the p element in #foo
  3. show p which is now a child of #bar

Questions

  • What determines the execution order of methods in jQuery chains?
  • How can I ensure that each event completes before the next starts?
+3  A: 

The expected result is correct. The observed behaviour may be a result of hide('slow') which runs asynchronously. So it runs while the next action executes. So it appears as if p is appended to #bar first. You can try hide() without the slow to see if that makes a difference.

Vincent Ramdhanie
Don't you mean *asynchronously*? (But the substance of your answer is correct.)
Sixten Otto
I believe he does mean asynchronously. I've observed similar behavior in some of my applications that have some sort of animation followed by DOM manipulation of either the same or a different node.
Justin Johnson
Aye...Asynchronous = At the same time is exactly what I meant. Will update accordingly.
Vincent Ramdhanie
+1 For async. It's exactly what I was gonna say.
hasen j
which reminds me of Joel's "Back to basics" and "Leaky abstractions" articles.
hasen j
+1  A: 

Pretty sure it's executed in the order you invoke it, it probably starts the hide part and a split second later it's appended to that other element but the animation part has already begun, it takes longer than a millisecond because you set it to 'slow' and it's jumping in opacity from 1 to 0, going from say 1 to .9 to .8 in milliseconds.

$.fn.hide = function() { alert('hiding'); return this; };
$.fn.appendTo = function() { alert('appending To') }
$('body').hide().appendTo('html')
meder
+6  A: 

To ensure you execute something AFTER an effect like hide or show, use a callback. http://docs.jquery.com/Effects/show#speedcallback

To Add:

Vincent is right, the execution is actually

  1. start hiding the p element in #foo (SLOWLY)
  2. append p to #bar (in a snap)
  3. start showing p which is now a child of #bar (SLOWLY)

What you saw was the result of the effect

  1. append p to #bar (executed)
  2. hide the p element in #foo (COMPLETED)
  3. show p which is now a child of #bar (COMPLETED)
o.k.w
+1  A: 

show() and hide() are actually animation effects but when no arguments are passed they use an "instant" duration. However, due to the fact that they're implemented as animations, that means that they don't execute synchronously with the function chain. Thus, what you really should be using is a callback off of the hide() call to trigger a callback function which calls append() and then show().

http://docs.jquery.com/Effects/show

http://docs.jquery.com/Effects/hide

Amber
+1  A: 

Hide is asynchronous. If you want to wait for it to finish, you need to put all the code you want to run after it into a callback function that you pass to hide as a parameter.

Gareth Simpson
+3  A: 

If you want to wait until each animation completes before doing the next step, use the animation callbacks detailed in the documentation:

$('#bar').click(function () {
  $('#foo p').hide('slow', function(){
    $(this).appendTo('#bar').show('slow');
  });
});
Sixten Otto
I like the idea of re-coding of the original codes to prevent the 'async' symptoms; yet maintaining a single statement.
o.k.w
A: 

You have to use callback if you want to queue the other methods run after the show hide thing like "Sixten Otto" has said. The animation of show hide will not wait for append method to execute. It starts a separate thread with setInterval while in the meantime, other code is invoked. So the result you got is not unexpected.

Mahbub