Creating jquery draggable “stop” callbacks in a fo

2019-08-23 05:59发布

问题:

I have a for loop iterating through a variable number of draggable elements, creating a stop-event callback for each. The callback function needs to know the index of the item that it is linked to.

I've encountered what I think is a closure problem: when the callbacks are fired off, the state of the loop iterator index variable that gets passed to the callback (_x) is the last value of the loop iterator index, rather than the value of that iterator index at the time the callback function was defined. Some coffeescript below:

for _x in [0..total]
  $(window).ready $(".draggable-#{_x}").draggable
    cursor: 'move'
    handle: ".draggable-image-move-button-#{_x}"
    containment: 'div#banner-builder-preview'
    stop: =>
      alert "x === #{_x}"

In the example above, the alert prompt will always print "x === total+1" rather than "x === 0", "x === 1"... etc.

What's the best solution to pass a unique index to the callback for each of the elements that have a stop event? I would prefer to do this without resorting to yet another jquery selector to pull more data from whatever element fired off the callback.

UPDATE:

Another problem I'm now running into: my callback, within the for loop, cannot see other functions defined in the file. I think that's very odd, even if the definition happens before the for loop is used to create the anonymous callback functions.

For example:

for _x in [0..total]
  do (_x) ->
    $(window).ready $(".draggable-#{_x}").draggable
      cursor: 'move'
      handle: ".draggable-image-move-button-#{_x}"
      containment: 'div#banner-builder-preview'
      stop: =>
        someFunction _x  # ... this line throws an exception, not defined
        alert "x === #{_x}

回答1:

The problem is that your callbacks:

stop: =>
  alert "x === #{_x}"

all end up referencing _x but they don't evaluate _x until they get called. By the time that happens, _x has the value total + 1. This is such a common issue that CoffeeScript has the do keyword to help:

When using a JavaScript loop to generate functions, it's common to insert a closure wrapper in order to ensure that loop variables are closed over, and all the generated functions don't just share the final values. CoffeeScript provides the do keyword, which immediately invokes a passed function, forwarding any arguments.

So you can say this instead:

for _x in [0..total]
  do (_x) ->
    $(window).ready $(".draggable-#{_x}").draggable
      #... as before