Search code examples
javascriptjqueryjquery-uijquery-resizable

How to call function with correct argument in jQuery-resizable object which is creating in a loop?


I've couple of resizable objects and I'd like to trigger function 'changeS()' when I stop resizing the object.
Function changeS() should takes as first argument the name of the object for which it's called.
I mean:
when I stop resizing object "#resizable0" then should be called changeS(#resizable0,new_size - old_size),
when I stop resizing object "#resizable1" then should be called changeS(#resizable1,new_size - old_size), etc.

There is everything OK when I just set in code this method for every object i have, but I'd like to do it in loop for(), because there will be much more objects, and then I have a problem:

var old_size=0;
var new_size=0;
var x,i;

for(i=0; i<5; i++){
x = "#resizable"+i;
var $res = $(x);

$res.resizable({
    grid: 22, minHeight:22, handles: 's',
    start: function(event,ui) {
        old_size = ui.originalSize.height;
    },
    stop: function(event,ui) {
        new_size = ui.size.height;

        if(new_size!=old_size)
            changeS($res,new_size - old_size);
    }
});
}

I don't know how to correctly pass a name of object as an argument of the function.
When function changeS() is executed it's always called with the same name - name of the last created object ("#resizable5" - in this case).

My question is:
How do I make for each object the function changeS() was called with the corresponding argument(name of this object)?

Thanks, popKonr


Solution

  • The functions you're creating in the loop are closures. They keep an enduring reference to the variables they close over, not a copy of the value as of when the function is defined. And so all of them see the last value you assign to $res, not the value during "their" loop.

    The usual way to solve this is to use a factory function, something like this:

    var x,i;
    
    for(i=0; i<5; i++){
        x = "#resizable"+i;
        var $res = $(x);
    
        makeResizeable($res);
    }
    
    function makeResizeable($thisres) {
        var old_size = 0,
            new_size = 0;
    
        $thisres.resizable({
            grid: 22, minHeight:22, handles: 's',
            start: function(event,ui) {
                old_size = ui.originalSize.height;
            },
            stop: function(event,ui) {
                new_size = ui.size.height;
    
                if(new_size!=old_size)
                    changeS($thisres,new_size - old_size);
            }
        });
    }
    

    Note how we now use $thisres, the argument passed into the factory function makeResizeable, rather than $res, since the argument baked into the functions we create within makeResizeable isn't changed. Also note I've moved new_size and old_size into that factory function as well. The event handlers will close over that data, and so each pair of them will get their own private copies of those variables.

    Closures are not complicated, but there are a few fundamental things about them people tend to misunderstand. Once you've got those down, though, you'll be in good shape.