0

I'm importing children of #parentEle, making a copy of each and pushing that copied object to array with new ID - #eleCopy(i)

I'm trying to also assign a click function to the original object imported. Clicking it would change CSS of its copy (referenced from the array). But the copy being affected is always the same one - last one loaded. I was hoping to reference it by its index number in the array... How do I "freeze" correct array reference in the click function so that $(this) 0 would corresponds to ar[0], $(this)` 1 to ar[1] and so on?

var ar = [];
var i = 0;

$('#parentEle').children().each(function() {

        ... // copy of $(this) is created: $('#eleCopy'+i)

        ar.push($('#eleCopy'+i));


        $(this).on('click', function() {

            ar[i].css({ ... });

        });

        i++;

});
user3024007
  • 283
  • 4
  • 12

3 Answers3

2

Please see these questions for an explanation of the problem:

In your case the solution is quite simple: Since you are already using .each, there is no need to keep an external counter. The current index is passed to the callback as argument:

// method signature: each( function(index, Element) )
$('#parentEle').children().each(function(i) {
    ... // copy of $(this) is created: $('#eleCopy'+i)
    ar.push($('#eleCopy'+i));

    $(this).on('click', function() {
        ar[i].css({ ... });
    });
});

However, the question is whether you even need to reference the clone via the array. For accessing the clone inside the event handler, you can just refer to it directly:

$('#parentEle').children().each(function(i) {
    var $copy = ...; // copy of $(this) is created: $('#eleCopy'+i)
    $(this).on('click', function() {
        $copy.css({ ... });
    });
});

This works because the event handler you bind is a closure.

A disadvantage of both of these solutions is that you are creating a new event handler in each iteration, even though they all do the same thing. You could leverage jQuery's .data API to store a reference to the clone with the original node and bind a single event handler to the elements:

$('#parentEle').children().each(function(i) {
    var $copy = ...; // copy of $(this) is created: $('#eleCopy'+i)
    $(this).data('copy', $copy);
}).on('click', function() {
    $(this).data('copy').css({ ... });
});
Community
  • 1
  • 1
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • the array serves other purposes also which is why I was hoping to utilize it for this purpose as well... I'm editing the question to clarify how $(this) is cloned as I'm still not getting a response on the click... – user3024007 Dec 18 '13 at 06:09
  • OK, but the point is that you don't have to use the array (or `i` for that matter) to refer to the cloned element. – Felix Kling Dec 18 '13 at 06:09
  • I got you now. It actually works as your second example. I had my #eleCopy being attached to yet another dynamically created object, it was that container object that had to be set to $copy. Thank you for your help and lots of reference. – user3024007 Dec 18 '13 at 06:18
1

This is a scope problem.

When the click event happens and ar[i].css({ ... }) is executed, the value of i already has been incremented by all the calls to

i++;

So i is equal to the number of children of #parentEle

In order to have the desired behavior behavior, wrap the code in a closure:

Replace

$(this).on('click', function() {
    ar[i].css({ ... });
});

with

(function(i) {
    $(this).on('click', function() {
        ar[i].css({ ... });
    });
})(i);

For more info about javascript closures, see this answer

Community
  • 1
  • 1
Aurélien Gasser
  • 3,043
  • 1
  • 20
  • 25
  • I'll go over your reference, scope is something I need to brush up on. Your example might've actually worked on closer examination of other bits of my code. Thank you – user3024007 Dec 18 '13 at 06:21
0

Why not:

$('#parentEle').find('[id^="eleCopy"]').each(function() {
    $(this).css({ ... });
}

each()

StartsWith

Felix
  • 37,892
  • 8
  • 43
  • 55