24

How do you replace an element in jQuery and have the replacement element returned instead of the element that was removed?

I have the following scenario. I have many checkboxes and once you click one of them, that checkbox is replaced by a loading icon. Once some AJAX stuff happens, the loading icon is replaced by a tick icon.

Using jQuery's replaceWith, you'd do something like:

$("input[type='checkbox']").click(function() {

  $(this).replaceWith("<img src='loading.jpg' alt='loading'/>");
  $.post("somepage.php");
  $(this).replaceWith("<img src='tick.jpg' alt='done'/>"); 

});

However, this doesn't work because replaceWith returns the element that was removed, not the one which was added. So after the AJAX stuff completes, loading.jpg will just stay there forever.

Is there some way I can return the replacement element without selecting it?

Thanks in advance.

Philip Morton
  • 129,733
  • 38
  • 88
  • 97

8 Answers8

22

Give the loading image a class, then in the post callback, use the class as a selector to find the image you've just injected.

$("input[type='checkbox']").click(function() {
  $(this).replaceWith("<img src='loading.jpg' alt='loading' class='loading-image' />");
  $.post("somepage.php", function() {
      $('.loading-image').replaceWith("<img src='tick.jpg' alt='done'/>");
  });
});

If you may have several of these running at a time, you can get the closest parent of this and use that as the context when searching for the class.

EDIT: Another alternative that uses a variable to store the new element and removes the need to apply the class and search for the new element when the function returns.

$("input[type='checkbox']").click(function() {
  var loading = $("<img src='loading.jpg' alt='loading' />");
  $(this).replaceWith(loading);
  $.post("somepage.php", function() {
      loading.replaceWith("<img src='tick.jpg' alt='done'/>");
  });
});
tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 4
    Instead of using a .loading-image class, couldn't you just keep the reference to the loading img and put that in the callback, instead re-finding it with $('.loading-image')? – cdmckay May 21 '09 at 13:55
  • The question (and answer) was specific to replaceWith which returns the element that was removed, not the element that was created. You could create the element and store a reference to it in a variable, then do the replacement. In that case, I think yes you could use the reference and not look it up again. – tvanfosson May 21 '09 at 14:08
13

Create an element and use it as parameter to replaceWith:


$('input[type=checkbox]').click(function() {
    var img = document.createElement('img');
    img.src = 'loading.jpg';
    $(this).replaceWith(img);
    $.post('somepage.php', function() {
        $(img).replaceWith('<img src="tick.jpg" alt="done"/>');
    });
});

Keeper
  • 3,516
  • 1
  • 18
  • 29
  • I like this, no cluttering with additional classes or ids, you know exactly which img to replace. Very clean. – Mario Menger May 21 '09 at 13:52
  • 1
    +1 Didn't see this until I had updated my answer with similar code. I'd still use jQuery though to create the new element. – tvanfosson May 21 '09 at 14:12
2

You could give it a unique id using the index:

$("input[type='checkbox']").click(function() {
  var index = $("input[type='checkbox']").index(this);
  $(this).replaceWith("<img src='loading.jpg' id='myLoadingImage" + index + "' alt='loading'/>");
  $.post("somepage.php");
  $('#myLoadingImage'+index).replaceWith("<img src='tick.jpg' alt='done'/>"); 

});
Jose Basilio
  • 50,714
  • 13
  • 121
  • 117
1

If there isn't a necessity to use specifically the replaceWith method - you could use the replaceAll method which is the opposite to replaceWith.

See the answer here: (answer to a similar question asked a year later) replaceAll replaces each element in the object passed as the parameter or the elements matching the passed selector as the parameter with the matched elements and returns the matched elements (the new content).

That way, the edit in tvanfosson's answer (and the code at Keeper's answer) would look like this:

$("input[type='checkbox']").click(function() {
    var loading = $("<img src='loading.jpg' alt='loading' />").replaceAll($(this));
    $.post("somepage.php", function() {
        loading.replaceWith("<img src='tick.jpg' alt='done'/>");
    });
});

It's only a line shorter but for brevity and to include the option to use replaceAll() I saw fit to add this answer to this old and more-viewed question.

Community
  • 1
  • 1
et_l
  • 1,868
  • 17
  • 29
0

I wrote a little plugin to achieve a similar result because I like to be able to retrieve a pointer for the new item in one line of code.

(function($){
    $.fn['overwrite'] = function(target){
        $(target).replaceWith(this);
        return this;
    }
}(jQuery));

Usage example:

$("input[type='checkbox']").click(function() {
    var $loading = $("<img src='loading.jpg' alt='loading' class='loading-image' />").overwrite( this );
    $.post("somepage.php", function() {
        $loading.replaceWith("<img src='tick.jpg' alt='done'/>");
    });
});
Femi
  • 1,332
  • 9
  • 20
0

The reason your code doesn't work is that the first replaceWith replaces the item that this refers to. The second replaceWith attempts to replace it again, but since it is already gone, there is nothing to replace. And the tick icon won't get shown.

Your best bet is to use the post callback function that tvanfosson suggests.

Mario Menger
  • 5,862
  • 2
  • 28
  • 31
0

Check this.

$.fn.replaceWithMod = function(obj) {
var $a = $(obj);
this.replaceWith($a);
return $a;  
};

var newObj  = $('a').replaceWithMod('<div>New Created Object</div>');
$(newObj).css('color','green');
mr_app
  • 1,292
  • 2
  • 16
  • 37
-1

Why not make an intermediate jquery object, like this?...

$("input[type='checkbox']").click(function() {
    var insertedElement = $("<img src='loading.jpg' alt='loading'/>");
    $(this).replaceWith(insertedElement);
    $.post("somepage.php");
    var anotherInsertedElement = $("<img src='tick.jpg' alt='done'/>");
    $(this).replaceWith(anotherInsertedElement);
    //do something with either of the inserted elements
});
belugabob
  • 4,302
  • 22
  • 22
  • Additionally, the reference to the original checkbox $(this) is lost once the the replaceWith method is called, so even after fixing the syntax errors, it doesn't work. – Jose Basilio May 21 '09 at 13:43
  • Also, $.post is asynchronous. – cdmckay May 21 '09 at 14:02
  • OK - I corrected the typo. HAving seen the post by tvanfosson, and re-read the original post, I now understand the intent of the original code and agree with your comments. – belugabob May 22 '09 at 07:39