Ok I dunno why this isn't working. The alert outside the droppable says 0 but the alert inside the droppable says undefined. Anybody have an idea as to why? I want to start keeping a running count to when a person drops an object into the match div. I dont want to define play inside droppable cause it'll reset to 0 every time someone drops the object. Anybody have any idea as to what I'm not doing right?

var play = [];
for (var i = 0; i < 10; i++) {
    play[i] = 0;
    alert(play[i]);
        $('.match').droppable({
            greedy: true,
            drop: function(event, ui) {
            alert(play[i]);
            } 
        });
}

Heres the fiddle Click Here

Dani AI

Generated

Short version: is correct about the scoping problem and about not repeatedly calling .droppable() on the whole $('.match') set inside a for loop. The fix is to bind a handler per DOM element (or capture the loop index), and to track state so you don't increment when the same draggable is still over the same droppable.

Example — modern (ES6) fix that binds each .match separately and uses block-scoped let so the index is captured per iteration:

var play = [];
for (let i = 0; i < $('.match').length; i++) {
  play[i] = 0;
  $('.match').eq(i).droppable({
    greedy: true,
    drop: function (event, ui) {
      play[i]++;             // safe: i is block-scoped
      $(this).text(play[i]);
    }
  });
}

Alternative — keep the counter on the droppable itself and prevent double-counting per draggable by storing the last drop target on the draggable (no globals):

$('.match').each(function () {
  $(this).data('count', 0)
         .droppable({
           greedy: true,
           drop: function (e, ui) {
             if (ui.draggable.data('lastDrop') !== this) {
               ui.draggable.data('lastDrop', this);
               var cnt = $(this).data('count') + 1;
               $(this).data('count', cnt);
               $(this).text(cnt);
             }
           }
         });
});

Notes and quick troubleshooting:

  • If you need to count only the first time ever for a droppable, use .one('drop', ...) or set a counted flag on the element.
  • If you must support very old browsers without let/const, use a closure/IIFE (the pattern showed).
  • Make sure jQuery UI is loaded and that the number of .match elements matches your loop.

Recommended Answers

All 3 Replies

Razor, it's not clear why you might want to define .droppable() in a loop and maintain an array of counters. Each .droppable() statement will override the previous one so only the last .droppable() will be in effect when the loop terminates.

The simple answer to your question is that alert(play[i]); gives undefined because, by the time the drop handler fires, the for(){...} loop has exited and i is one greater than the maximum loop value.

I expect you want to alert the value associated with i at the time the .droppable() is put in place, in which case you can trap i in a closure as follows:

$(function () {

  var play = [];

  //This function exists solely to trap `i` in a closure
  function make_drop_fn(i) {
    return function (event, ui) {
      alert(play[i]);
    }
  }

  $('.drag').draggable();

  for (var i = 0; i < 3; i++) {
    play[i] = i;
    alert(play[i]);
    $('.match').droppable({
      greedy: true,
      drop: make_drop_fn(i)
    });
  }
});

DEMO

As you will see, I initialized the counters with play[i] = i rather than 0, so you can see which counter is alerted. You will also see that this value is always the highest value of i prior to meeting the loop's exit condition.

If you want to maintain counters for several droppable elements, then you can do so as follows :

$(function () {
  var play = [];

  function make_drop_fn(i) {
    return function (event, ui) {
      play[i]++;
      $(this).text(play[i]);
    }
  }

  $('.drag').draggable();

  $('.match').each(function (i, m) {
    play[i] = 0;
    $(m).droppable({
      greedy: true,
      drop: make_drop_fn(i)
    });
  });
});

DEMO

Other possibilities exist to meet thne same end.

Wow Thanks so much Airshow, big help for sure. One question, say if you didn't want the draggable div to keep counting once it was already over the droppable div.... how would you adjust the fiddle to stop counting.

Razor, aha I wondered if you might ask that.

The simplest approach is to inhibit the counts incrementing when a drop is made on the same target as the previous drop. So you need to track which .match element was last dropped on, and test accordingly.

$(function(){
    var play = [];
    var lastDropTarget;

    function make_drop_fn(i) {
      return function (event, ui) {
        if(this != lastDropTarget) {
          play[i]++;
          $(this).text(play[i]);
          lastDropTarget = this;
        }
      }
    }

    $('.drag').draggable();

    $('.match').each(function (i, m) {
      play[i] = 0;
      $(m).droppable({
        greedy: true,
        drop: make_drop_fn(i)
      });
    });
});

DEMO

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.