Drag&Drop online-TypeError: piece.contains is not a function

Hello, I’m trying to modify code in this online demo by @dvbridges to allow drag and drop. https://gitlab.pavlovia.org/demos/draganddrop
There will be four boxes and four images on the screen. Subjects need to drag the images to the boxes. In the final data file, the box each image has been chosen will be recorded. I have the following JS code (manually created based on the above demo) but on Pavlovia it says “Type error: piece.contains is not a function” (“piece” refers to the image to be moved). And this error is from this line of code in each frame tab:

if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && grabbed === 'undefined')

Anything wrong with my JS code below?

# Begin Experiment
movePicked_sl4 = function(picked, mouseSL4, grabbed) {
  if (grabbed != 'undefined' && mouseSL4.getPressed()[0] === 1) {
    grabbed.pos = mouseSL4.getPos();
    return grabbed
  } else {
      for (let piece of picked) {
        if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && grabbed === 'undefined'){
          piece.pos = mouseSL4.getPos();
          return piece;
        }
      }
   return 'undefined'
  }
}

# Begin Routine
pieces = [image41, image42, image43, image44];
picked = [];
movingPiece = 'undefined';
finalBoxChosen = 0;
finalBoxChosen_data = [];

# Each Frame
for (let piece of pieces) {
    if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && movingPiece === 'undefined') {
        picked.push(movingPiece)
    }
}
movingPiece = movePicked_sl4(picked, mouseSL4, movingPiece);

# End Routine
for (var piece, _pj_c = 0, _pj_a = pieces, _pj_b = _pj_a.length; (_pj_c < _pj_b); _pj_c += 1) {
    piece = _pj_a[_pj_c];
    for (var i = 0, _pj_d = 4; (i < _pj_d); i += 1) {
        if (piece.contains(boxesPosSL4[i])) {
            finalBoxChosen = (i + 1);
        } else {
            finalBoxChosen = 0;
        }
    }
    finalBoxChosen_data.append(finalBoxChosen);
}
thisExp.addData("finalBoxChosen", finalBoxChosen_data);
console.log("finalBoxChosen", finalBoxChosen_data);

@mengsili I am not sure what would cause the error, but perhaps you could print the pieces to see if they are actually image stimuli, that should have a contains method. E.g.,

# Each Frame
for (let piece of pieces) {
    console.log(piece);
    if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && movingPiece === 'undefined') {
        picked.push(movingPiece)
    }

Thanks for the suggestion! I now realized that the error came from the function in the Begin Routine Tab. I think it might be sth wrong with “let piece of picked” because initial value of “picked” was set to be “picked = ”. I tried to change it to “picked = [image41, image42, image43, image44]”. This time the error appeared after I moved one image (before the change I couldn’t even move one image). I’d appreciate if you have any further insight into this problem!

      for (let piece of picked) {
        if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && grabbed === 'undefined'){
          piece.pos = mouseSL4.getPos();
          return piece;
        }

I modified the code in the each frame tab–picked.push(movingPiece) is changed to picked.push(piece). Seems the drag and drop thing works now! Is there any other potential problem? Thanks!

# Each Frame
for (let piece of pieces) {
    if (piece.contains(mouseSL4) && mouseSL4.getPressed()[0] === 1 && movingPiece === 'undefined') {
        picked.push(piece) 
    }
}

Are you sure you want to add piece to picked every frame rather than only if it hasn’t yet been picked?

Hi Wake–Honestly I don’t fully understand the whole logic of the “drag and drop” code but I modified it and it worked. I do feel I only need to add “piece” to picked only if it hasn’t yet been picked (that’s what the original code picked.push(movingPiece) did I suppose?). But I need a way now to solve the problem of “piece.contains is not a function” so the above solution is I could think of. Do you have any suggestion?

Sorry for multiple questions recently. Actually I now encountered another error with my code to record which box each image has finally been dropped into. There are four boxes at designated positions and four images to drag and drop. So my original idea is to judge which box position (the central point) is contained in which image, and then record the number of the box as the chosen box for each image. It works locally but online it gives me (When I deleted the code the error disappeared so I’m sure this caused the error). Thanks for any suggestion on how to implement this online.

My Python code:

# End Routine Tab
boxesPosSL4 = [(-0.6, 0.3), (-0.2, 0.3), (0.2, 0.3), (0.6, 0.3)]
for piece in pieces:
    for i in range(4):
        if piece.contains(boxesPosSL4[i]):
        # this seems to be the line of code that causes the error
            finalBoxChosen = i+1
        else:
            finalBoxChosen = 0
    finalBoxChosen_data.append(finalBoxChosen)

# save box chosen data
thisExp.addData('finalBoxChosen',finalBoxChosen_data)
print('finalBoxChosen',finalBoxChosen_data)

My JS code:

boxesPosSL4 = [(-0.6, 0.3), (-0.2, 0.3), (0.2, 0.3), (0.6, 0.3)];
for (let piece of pieces) {
    for (var i = 0, _pj_d = 4; (i < _pj_d); i += 1) {
        if (piece.contains(boxesPosSL4[i])) {
            finalBoxChosen = (i + 1);
        } else {
            finalBoxChosen = 0;
        }
    }
    finalBoxChosen_data.push(finalBoxChosen);
}
thisExp.addData("finalBoxChosen", finalBoxChosen_data);
console.log("finalBoxChosen", finalBoxChosen_data);

The each frame code only adds a piece to the “picked” list if there is not already a piece picked and moving. This code was to ensure that multiple pieces could not be picked up under a single click.

Thanks for the explanation! The current code I have seems fine. Hopefully there won’t be other issues!

1 Like