2

I have a simple front-end in jQuery/HTML5 (+ backend-generated code which does not bring the issue, so I will omit it). The currently-in-use jQuery version is 1.8.3 and no version collision exists (i.e. no other jQuery version is loaded - it happened many times in other systems here).

The front-end invokes the following routines:

detailAjaxCall("\/client\/orders\/detailsLoad\/id\/3");

$(".upLink").click(function(){
    console.log("subiendo");
    var id = $(this).closest("tr").data('detail-id');
    var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
    detailAjaxCall(url);
    return false;
});

$(".downLink").click(function(){
    console.log("bajando");
    var id = $(this).closest("tr").data('detail-id');
    var url = "\/client\/orders\/detailMoveDown" + "/id/" + id;
    detailAjaxCall(url);
    return false;
});

$(".delLink").click(function(){
    console.log("borrando");
    var id = $(this).closest("tr").data('detail-id');
    var url = "\/client\/orders\/detailDelete" + "/id/" + id;
    detailAjaxCall(url);
    return false;
});

Note: the url string are not malformed. they are generated by a json exporter (this chunk of code was extracted from the view source option in Google Chrome browser). Evaluating any of them will return a string with no backslashes.

The detailAjaxCall("/client/orders/detailsLoad/id/<number>") actually works: it returns the expected json code when I hit the url, and renders the appropiate table items:

function detailAjaxCall(url)
{
    $.get(
        url,
        {},
        function(data, status, xhr) {
            //todo refrescar(data);
            var table = $("#detail-list");
            table.empty();
            if (data.length == 0) {
                $("<tr></tr>").addClass("empty").append($("<td></td>").addClass("empty").text("No hay detalles para este pedido")).appendTo(table);
            } else {
                $.each(data, function(index, element) {
                    $("<tr></tr>")
                        .data('detail-id', element['id'])
                        .append(
                            $("<td></td>")
                                .append(
                                    $("<span></span>").addClass("product-name").text(element['producto_nombre'])
                                )
                                .append("<br />")
                                .append(
                                    $("<span></span>").addClass("product-dims").text(
                                        "Ancho: " + element['ancho'] +
                                        ", Largo: " + element['largo'] +
                                        ", Calibre: " + element['calibre']
                                    )
                                )
                        )
                        .append($("<td></td>").addClass("quantity").text(element['cantidad']))
                        .append($("<td></td>").addClass("price").text(element['precio']))
                        .append(
                            $("<td></td>")
                                .append(
                                    $("<a></a>").addClass("upLink").text("subir").attr("href", "javascript: void 0")
                                ).append(" ")
                                .append(
                                    $("<a></a>").addClass("downLink").text("bajar").attr("href", "javascript: void 0")
                                ).append(" ")
                                .append(
                                    $("<a></a>").addClass("delLink").text("eliminar").attr("href", "javascript: void 0")
                                ).append(" ")
                        )
                        .appendTo(table);
                });
            }
        },
        'json'
    ).fail(function(){
        $("#ajaxDetailErrorDialog").dialog("open");
    });
}

Pay attention to the generation of the "<a></a>" since my problem is with them. They all have classes like delLink, upLink and downLink.

My issue starts here: calling $(".delLink").click(callback), $(".upLink").click(callback), $(".downLink").click(callback) does not seem to bind the events to the newly created items (althought they are created inside the ajax call). Seeing the source code for the click method, passing parameters, is like a call to on.

So: what am I doing wrong to bind the event dynamically, so newly created elements trigger my events as well?

Gabriele Petrioli
  • 173,972
  • 30
  • 239
  • 291
Luis Masuelli
  • 10,846
  • 9
  • 41
  • 80
  • 1
    By reading only the title, i think you should delegate event... https://learn.jquery.com/events/event-delegation/ – A. Wolff Apr 28 '14 at 16:15
  • 1
    its because when you applied the classes to those elements they did not exist in the page so you have to apply the binding events to the newly added items again after they have been added to the dom. – Farhad-Taran Apr 28 '14 at 16:16
  • 3
    @xerxes or better, delegate event to avoid multiple identic handlers – A. Wolff Apr 28 '14 at 16:17
  • Now I got it. Yes, you're right, but I misunderstood how the underlying .on call worked (I know I should delegate with .on, but misunderstood how the call was performed). – Luis Masuelli Apr 28 '14 at 16:17

1 Answers1

6

You need to dynamically delegate the click handlers because you assign your click handlers before the new elements are created.

For example, delegate to the document:

$(document).on('click', '.upLink', function(){
    console.log("subiendo");
    var id = $(this).closest("tr").data('detail-id');
    var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
    detailAjaxCall(url);
    return false;
});

This works because all clicks on the document will be checked by this handler, to see if they match .upLink. Even if you create new elements after this is assigned, the clicks still pass through this event.

MrCode
  • 59,851
  • 9
  • 76
  • 106
  • Yep, read the jquery source and docs again, and noticed that the .on call was different (i.e. a non-dynamic one). Accepting this in 10 minutes... – Luis Masuelli Apr 28 '14 at 16:18
  • 2
    +1 but it would be better to use `$("#detail-list").on("click", ".uplink"...` - the closer you attach the event handler the better. – Reinstate Monica Cellio Apr 28 '14 at 16:20
  • 2
    @LuisMasuelli, see this answer for more details about the different ways to use `.on`: http://stackoverflow.com/questions/1525664/jquery-how-to-bind-onclick-event-to-dynamically-added-html-element/17086311#17086311 – MrCode Apr 28 '14 at 16:20
  • @Archer that would work too and you're right, the closer the better. – MrCode Apr 28 '14 at 16:23