views:

103

answers:

3

Hi,

I'm trying to loop through an array "fSel.sI", and based on the data inside, pass them as values (not reference) on to a number of function declarations. Right now the problem is that mydrag contains a reference, and as draggable gets called it uses the last array item data. Hence when start: drag: stop: is called, the values are not unique. Help?

makeDraggable : function() {
     // create new draggable
     for (var i = 0; i < fSel.sI.length; i++) {
      mydrag = fSel.sI[i];
      $("#" + mydrag).draggable({
       cancel: [''],
       distance: 5,
       containment: "#fWorkspace",
       handle: mydrag,
       start: function() { dragRegister(mydrag)},
       drag: function() { dragItems(mydrag)},
       stop: function() { dragStop(mydrag)}
      });
     }
    },
+1  A: 

This is because your creating a closure. Try this instead

makeDraggable : function() {
        // create new draggable
        for (var i = 0; i < fSel.sI.length; i++) {
                mydrag = fSel.sI[i];
                $("#" + mydrag).draggable({
                        cancel: [''],
                        distance: 5,
                        containment: "#fWorkspace",
                        handle: (function(mydrag){return mydrag;}(mydrag)),
                        start: (function(mydrag){return function() { dragRegister(mydrag)};}(mydrag)),
                        drag: (function(mydrag){return function() { dragItems(mydrag)};}(mydrag)),
                        stop: (function(mydrag){return function() { dragStop(mydrag)};}(mydrag))
                });
        }
    },

Take a look at this video to understand the powerful and usefulness of Javascript closures: http://vimeo.com/1967261

Gary Green
See Steve's comment for a better way of doing this :)
Gary Green
A: 

Try

function dragIt(mydrag) {
      $("#" + mydrag).draggable({
              cancel: [''],
              distance: 5,
              containment: "#fWorkspace",
              handle: mydrag,
              start: function() { dragRegister(mydrag)},
              drag: function() { dragItems(mydrag)},
              stop: function() { dragStop(mydrag)}
      });
}

//Elsewhere ...

makeDraggable : function() {
        // create new draggable
        for (var i = 0; i < fSel.sI.length; i++) {
                dragIt(fSel.sI[i]);

        }
    },
Inaimathi
+3  A: 

You should look into closures.

Try the following code:

makeDraggable : function() {
        // create new draggable
        for (var i = 0, l = fSel.sI.length, sI = fSel.sI; i < l; i++) {
                var mydrag = sI[i];
             (function(mydrag) {
                            $("#" + mydrag).draggable({
                                    cancel: [''],
                                    distance: 5,
                                    containment: "#fWorkspace",
                                    handle: mydrag,
                                    start: function() { dragRegister(mydrag); },
                                    drag: function() { dragItems(mydrag); },
                                    stop: function() { dragStop(mydrag); }
                            });
          })(mydrag);
         }
},

Your problem is with the start, drag, and stop functions. They don't execute immediately; by the time they do, mydrag has been set to another value. By wrapping a self-executing function around the code block containing these functions, we create a closure, where mydrag doesn't change.

Note: For performance reasons, when accessing properties of an object more than once, it's best to create a variable that references (or holds) the property. In your for loop, I've created two variables l and sI that store fSel.sI.length and fSel.sI (respectively) so that JavaScript doesn't have to look up the sI and length properties every time around the loop.

Steve Harrison
Thanks so much for this!!! It works perfectly.
Jakub