eqiz eqiz - 1 month ago 7x
Javascript Question

Jquery .data keeps going undefined on draggable element in droppable area

I been struggling with this all morning and I can't seem to figure out what the issue is. On my page I have a draggable text field. (It has a static ID for testing purposes when you first drag it to the canvas as "item-text-1")

When I first drag the item down in the console log you can clearly see a "1" being outputed from the $("#item-text-1").data("test") however as you continue to drag it around a few times, it will start to go to "undefined" for some reason and I have absolutely no idea how to fix this. I tried doing ui.helper.detached() then also removed that all together and I'm just lost at this point.


<script src="//code.jquery.com/jquery-1.12.4.js"></script>
<script src="//code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css" />

<script type="text/javascript">
$( function() {

function setDraggable(el, doClone) {
helper: doClone ? 'clone' : "original"

accept: ".draggable",
drop: function( event, ui ) {
cloned = ui.helper.clone();
var applyData = false;
if (ui.draggable.hasClass('cloned') == false) {
applyData = true;
console.log("this is a clone!");

setDraggable(cloned, false)
cloned.attr("id", "item-text-1");

$( this )

if (applyData == true) {
$("#item-text-1").data("test", "1");
} else {

var newTop = $(ui.helper).offset().top - $(this).offset().top;
var newLeft = $(ui.helper).offset().left - $(this).offset().left;

cloned.css("top", newTop);
cloned.css("left", newLeft);

accept: ".draggable",
drop: function( event, ui ) {
setDraggable($(".draggable"), true);

$( "#tab-container" ).tabs();
} );

<body style="text-align:center; margin: 0;">

<div id="template-container" style="width: 880px; height: 775px; background-color: #f3f3f3; margin: auto; border: 3px solid #636363; padding: 8px;">

<div id="roles-tabs" style="padding-left: 0px; height: 90px; width: 100%; border-bottom: 1px solid #ddd;">
<div id="button-container" style="float: left; width: 60px; height: 80px; display: block;">&nbsp;</div>

<div id="tab-container" style="width: 600px; float: left; padding-right: 5px; padding-top: 5px; height: 80px;">
<li><a href="#tabs-formfields">Data Fields</a></li>
<div id="tabs-formfields">
<td style="padding-right: 6px;">
<div id="item-textfield" name="item-textfield" class="items-textfield draggable draggable-item" style="width: 100px; height: 30px; cursor: pointer; background-color: black; color: white; z-index: 99999">
<div class="new-textfield-field">
<div class="fake-data">Text Field</div>


<div id="showPDF" class="pdf-container pagesWrapper" style="z-index: 0; padding-top: 50px; width: 880px; padding-bottom: 50px; height: 515px; overflow-y: auto; background-color: #565656;">
<div style="margin: auto;" class="canvas">

<div id="page1" class="pages" style="position: relative; margin: auto; width: 400px; height: 500px; background-color: white;">

</div><br /><br />





Ok, so here's why the current implementation is not working: You have two droppable areas:

  1. When you drag the element the first time, a clone is created by jquery ui and dropped into the page container. Let's say clone "no-id". Then in the page droppable, the "no-id" is again cloned by this

    cloned = ui.helper.clone(); ... cloned.attr("id", "item-text-1");

Now you have on the dom two elements "no-id" and "item-text-1". When you assign the data to the element by id "item-text-1" this data will be assigned to the single element with that id.

After this handler finishes, you have the second droppable handler, that will also perform his logic upon the "no-id" element and remove it from the dom.


Now you have only one clone remaining : "item-text-1".

  1. When you drag the remaining clone the second time you will create another clone because of the page droppable handler, and you will have two elements with the id "item-text-1".

When you display now the data in the "item-text-1" element, because byId will return the first occurrence, you will see the data set before, in the first drag. The second element has no data attached to it.

When the pagesWrapper droppable will be invoked, then first element with the id "item-text-1" will be removed, and you will only have the last element available in the dom, that does not have the data anymore.

And from here on the data is lost.

In order to preserve the data, you will need to assign it to the new object after you clone the source object:

cloned = ui.helper.clone();
cloned.data('test', ui.helper.data('test'));

In this way you will always have the data with you, but be aware that your text control is always a new one, not the original clone.