31

I am appending content to a list using:

  $('a.ui-icon-cart').click(function(){
         $(this).closest('li').clone().appendTo('#cart ul');
  });

I want to perform further functions to the appended content (change class, apply animations etc)

How can I perform a callback on this function that will allow me to perform functions on the appended data?

user342391
  • 7,569
  • 23
  • 66
  • 88

4 Answers4

26

jQuery's .each() takes a callback function and applies it to each element in the jQuery object.

Imagine something like this:

$('a.ui-icon-cart').click(function(){
  $(this).closest('li').clone().appendTo('#cart ul').each(function() {
    $(this).find('h5').remove(); 
    $(this).find('img').css({'height':'40px', 'width':'40px'});
    $(this).find('li').css({'height':'60px', 'width':'40px'});
  });
});

You could also just store the result and work on it instead:

$('a.ui-icon-cart').click(function(){
  var $new = $(this).closest('li').clone().appendTo('#cart ul')
  $new.find('h5').remove(); 
  $new.find('img').css({'height':'40px', 'width':'40px'});
  $new.find('li').css({'height':'60px', 'width':'40px'});
});

I would also suggest that instead of mofiying the CSS like that you just add a class to your cloned li like this:

$(this).closest('li').clone().addClass("new-item").appendTo('#cart ul');

Then setup some styles like:

.new-item img, .new-item li { height: 40px; width: 40px; }
.new-item h5 { display: none }
gnarf
  • 105,192
  • 25
  • 127
  • 161
  • Hmm.. the 2nd example would still fire off asynchronously and wouldn't work in most cases when they needed the callback to happen *after* the appendTo – Trip Jul 09 '12 at 14:23
  • 1
    I'm not sure you understand what asynch means @Trip --- All that code above is synch. There are no asynchronous calls in this code. Even the callback inside the each is still synchronous, it is called immediately. The only difference in either example is that the first creates a new scope inside the each that you can have to store variables for each matched element, otherwise they should work identically. – gnarf Jul 09 '12 at 15:25
  • You are not entirely wrong, the appendTo happens asynchronously because the rest of the callback does not wait for the DOM... DOM operations are often faster then the script though.. – douwe Mar 05 '13 at 06:20
  • 1
    @douwe hate to burst your bubble, but the `appendTo` is also sync. It won't paint until the end of the current frame, but it happens immediately. If you access the element's parent, it will be that UL, if try to access it's css it will cause a reflow, etc. Trust me (read my profile), or research for yourself. – gnarf Mar 06 '13 at 03:33
  • 1
    @douwe Also, DOM operations are often the bottleneck in JavaScript performance, they are not by any means fast... – gnarf Mar 06 '13 at 03:34
  • @gnarf I have an [open question](http://stackoverflow.com/q/20942855/388639) related to a chained `addClass()` not triggering a CSS transition. You seem to be knowledgeable regarding jQuery, synchronicity, and browser rendering; would you mind taking a look (particularly at my comment on [this answer](http://stackoverflow.com/a/20943707/388639))? Thanks. – Michael Martin-Smucker Jan 06 '14 at 14:13
  • @gnarf, Hate to burst all your bubbles, but testing proves that a large append is absolutely NOT synchronous. – PRMan Aug 09 '22 at 17:22
15

Unfortunatly adding callbacks to dom operations is not something that can be done in a neat fashion using javascript. For this reason it is not in the jQuery library. A settimeout with the timer "1ms" however always puts the function in the settimeout on the bottom of the call stack. This does work! The underscore.js library uses this technique in _.defer, which does exactly what you want.

$('a.ui-icon-cart').click(function(){
    $(this).closest('li').clone().appendTo('#cart ul');
    setTimeout(function() {
        // The LI is now appended to #cart UL and you can mess around with it.
    }, 1);
});
douwe
  • 1,305
  • 10
  • 12
4

You can just keep chaining further operations at the semicolon.

$(this).closest('li').clone().appendTo('#cart ul').addClass('busy').fade('fast');

etc

Community
  • 1
  • 1
Scott Evernden
  • 39,136
  • 15
  • 78
  • 84
  • 2
    How would I implement this into a chain: $(this).find('h5').remove(); $(this).find('img').css('height','40px', 'width','40px' ); $(this).find('li').css('height','60px', 'width','40px' ); – user342391 Jun 24 '10 at 21:07
  • 1
    This does not help, the question is how you can apply a callback after the append operation is performed. DOM operations are performed asynchronously. So in the next part of the chain the appended content is not yet visible. – douwe Mar 05 '13 at 06:12
0

In jquery you could use $() just after your appending contents code. This way you can be sure that the content is loaded and the DOM is ready before performing any tasks on the appended content.

$(function(){
//code that needs to be executed when DOM is ready, after manipulation
});

$() calls a function that either registers a DOM-ready callback (if a function is passed to it) or returns elements from the DOM (if a selector string or element is passed to it)

You can find more here
difference between $ and $() in jQuery
http://api.jquery.com/ready/

AJchandu
  • 141
  • 2
  • 9