tags:

views:

12633

answers:

5

I'm trying to reorganize my code and make it more unobstrusive.

Mainly, there is a link for performing an action. If you click on the link, the action is performed via ajax (the ajax code is not on the code here) and the text is replaced by other text, offering the user the ability of undo the action. If the user click the Undo link, ajax restore the previous situation and the text offering the user perform the action it's shown again.

I've made a simpler version of my problem in a single html file (code below). There are two paragraphs. If you click on the link of the first paragraph, which uses inline onclick call, the code works as expected. But if you click on the link of the second paragraph, which uses jQuery onclick function, the Undo link doesn't work, and I can't figure out why.

Any help? Thanks a lot!

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <title></title>
    <script src="http://code.jquery.com/jquery-latest.js"&gt;&lt;/script&gt;
    <script>
     $(function() {  
      $(".add2").click(function(){
       var placeId = $(this).parents(".place").attr("id");
       $("#" + placeId + " .add2").remove();
       $("#" + placeId + " .actions").append("<span class=\"added\">Added!</span> <a class=\"undoadd2\" href=\"#\">Undo</a>");
       return false;
      })
      $(".undoadd2").click(function(){
       var placeId = $(this).parents(".place").attr("id");
       $("#" + placeId + " .actions").find("span.added, a.add2").remove();
       $("#" + placeId + " .actions").append("<a class='add2' onclick='copyPlace(\"" + placeId + "\"); return false;' href='#'>Add place</a>");
       return false;
      })
     });

     function addPlace(placeId){
      $("#" + placeId + " .add").remove();
      $("#" + placeId + " .actions").append("<span class=\"added\">Added!</span> <a class=\"undoadd\" href=\"#\" onclick=\"undoAddPlace('" + placeId + "'); return false;\">Undo</a>");
      return false;
     }

     function undoAddPlace(placeId){
      $("#" + placeId + " .actions").find("span.added, a.undoadd").remove();
      $("#" + placeId + " .actions").append("<a class='add' onclick='addPlace(\"" + placeId + "\"); return false;' href='#'>Add place</a>");
      return false;
     }
    </script>

</head>
<body id="home">
    <div class="place" id="3435910">
     <p class="actions"><a href="#" onclick="addPlace('3435910'); return false;" class="add">Add place</a></p>
    </div>

    <div class="place" id="3435912">
     <p class="actions"><a href="#" class="add2">Add place</a></p>
    </div>
</body>
</html>
A: 

Since you are dynamically adding new items to the DOM, you will have to register the click handler again on the new items. jQuery sets the handler when you call $('...').click(...).

Daniel A. White
+6  A: 

Since you are dynamically adding new items to the DOM, you will have to register the click handler again on the new items.

You can use http://docs.jquery.com/Events/live for this.

Wolfr
Live will only work if the selector is exactly the same. In this case I think the selector is unique to each DOM element.
tvanfosson
Thanks @wolfr. I didn't know this existed.
Daniel A. White
A: 

The click handler isn't being picked up when you append the element to the DOM. Try using jQuery to register the click handler by changing the line(s) that look like:

$("#" + placeId + " .actions").append("<span class=\"added\">Added!</span> <a class=\"undoadd\" href=\"#\" onclick=\"undoAddPlace('" + placeId + "'); return false;\">Undo</a>")

To

$("#" + placeId + " .actions").append("<span class=\"added\">Added!</span> <a class=\"undoadd\" href=\"#\" >Undo</a>");
$('#' + placeId + " .actions").find("span:last").find("a").click( function() {
    undoAddPlace( placeId );
    return false;
});

You might be able to do it more simply, but it looks like you could be adding more than one span to the paragraph so I went conservative. You could also probably chain off append, but I thought the reselect made the point clearer.

tvanfosson
+1  A: 

Wow!! Thanks to all of you!

I've read about the live event and final working code is:

$(function() {   
      $(".add2").click(function(){
       var placeId = $(this).parents(".place").attr("id");
       $("#" + placeId + " .add2").hide();
       $("#" + placeId + " .actions").append("<span class=\"added\">Added!</span> <a class=\"undoadd2\" href=\"#\">Undo</a>");
       return false;
      })
      $('.undoadd2').live('click', function(){ 
       var placeId = $(this).parents(".place").attr("id");
       $("#" + placeId + " .actions").find("span.added, a.undoadd2").remove();
       $("#" + placeId + " .add2").show();
       return false;
      });

Previously I used remove() for delete the text that offers to perform the action. I've changed it for hide/show so I don't have to use live also in the first function.

Thanks again!

ana
+3  A: 

Just demonstrating the use of .end() for a more fluid solution:

$(function() {          
    $(".add2").click(function(){
      return $(this).parents(".place")
       .find(".add2")
        .hide()
       .end()
       .find(".actions")
        .append("<span class=\"added\">Added!</span> <a class=\"undoadd2\" href=\"#\">Undo</a>");
    });
    $('.undoadd2').live('click', function(){ 
      return $(this).parents(".place")
       .find(".actions")
        .find("span.added, a.undoadd2")
         .remove()
        .end()
       .end()
       .find(".add2")
        .show();
    });
});
ken