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

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.
<html>
<head>
<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) {
el.draggable({
helper: doClone ? 'clone' : "original"
});
}
$(".pages").droppable({
accept: ".draggable",
drop: function( event, ui ) {
cloned = ui.helper.clone();
cloned.removeClass('ui-resizable');
var applyData = false;
if (ui.draggable.hasClass('cloned') == false) {
applyData = true;
console.log("this is a clone!");
cloned.addClass('cloned');
}
cloned.find(".ui-resizable-handle").remove();
cloned.find(".ui-resizable").remove();
cloned.find("resizable").remove();
cloned.removeClass('ui-resizable');
cloned.resizable().draggable();
setDraggable(cloned, false)
cloned.attr("id", "item-text-1");
$( this )
.append(cloned)
if (applyData == true) {
$("#item-text-1").data("test", "1");
console.log($("#item-text-1").data("test"));
} else {
console.log($("#item-text-1").data("test"));
}
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);
//ui.helper.detach();
}
});
$(".pagesWrapper").droppable({
accept: ".draggable",
drop: function( event, ui ) {
ui.helper.remove();
}
})
setDraggable($(".draggable"), true);
$( "#tab-container" ).tabs();
} );
</script>
</head>
<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;"> </div>
<div id="tab-container" style="width: 600px; float: left; padding-right: 5px; padding-top: 5px; height: 80px;">
<ul>
<li>Data Fields</li>
</ul>
<div id="tabs-formfields">
<table>
<tbody>
<tr>
<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>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</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 />
</div>
</div>
<br/>
<br/>
<br/>
<br/>
</div>
</body>
</html>

Ok, so here's why the current implementation is not working:
You have two droppable areas:
$(".pages").droppable({
...
and
$(".pagesWrapper").droppable({
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.
ui.helper.remove();
Now you have only one clone remaining : "item-text-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.

Related

How To Get All Text Content from Duplicate Parent Class with Different Id?

I am trying to get all text content of a parent class detected by addEventListener.
The code I am using is-
document.addEventListener('click',function (event) {
var text = document.getElementsByClassName(event.target.parentElement.parentElement.parentElement.className);
console.log(text[0].innerText);
}, false);
but the problem is, where I am trying to apply the code, has same class name with different id, for example-
<div class="sc-EHOje gMuVTh" size="24" role="group" aria-labelledby="extraTitle_70356820.0">
and
<div class="sc-EHOje gMuVTh" size="24" role="group" aria-labelledby="extraTitle_70356820.1">
has same ClassName (sc-EHOje gMuVTh) but different id (extraTitle_70356820.1 and extraTitle_70356820.0), so if I use only ClassName I always get text for id extraTitle_70356820.0, even if I click for extraTitle_70356820.1.
How can I get all text from the class of the id I clicked when there is duplicate class name? Is there a way to incorporate the ClassName and id at the same time to get the text from parent?
I changed my initial answer because I realised I think you want the uppermost parent text too as well as the text of the element you clicked on? Not sure if this answer is the correct for you but it's my take on things.
//var classname = document.getElementsByClassName("sc-EHOje gMuVTh");
//for (var i = 0; i < classname.length; i++) {
// classname[i].addEventListener('click', function() {
// console.log(this.parentElement.parentElement.parentElement.innerText);
// });
//}
// 24/09 edit
var target = "sc-EHOje gMuVTh";
document.addEventListener('click', function(event) {
if (event.target.className === target) {
var parentElemText = event.target.parentElement.parentElement.parentElement.innerText;
console.log(parentElemText);
}
});
.upperMostParent {
width: 200px;
height: 200px;
background: red;
margin: 1rem;
position: relative;
}
.secondUpper {
width: 100px;
height: 100px;
background: blue;
margin: 1rem;
padding: 1rem;
}
.thirdUpper {
width: 70px;
height: 50px;
background: green;
padding: 1rem;
}
.sc-EHOje.gMuVTh {
background: yellow;
width: 50px;
height: 50px;
}
<div class="upperMostParent">
Parent text content 1
<div class="secondUpper">
<div class="thirdUpper">
<div class="sc-EHOje gMuVTh" size="24" role="group" aria-labelledby="extraTitle_70356820.0">Some text content</div>
</div>
</div>
</div>
<div class="upperMostParent">
Parent text content 2
<div class="secondUpper">
<div class="thirdUpper">
<div class="sc-EHOje gMuVTh" size="24" role="group" aria-labelledby="extraTitle_70356820.1">Some more text</div>
</div>
</div>
</div>

jQuery | detach(), append() scope

I have a main-div and two divs with the class of container. The div with the class of container has a child div with a class of content with different contents. I'd like for the user to click on their choice of containers and transport its content to main-div. Then when the user clicks on the main-div, I'd like to transport that content back to its original div.
I'm not sure how to detach the content from main-div once it's been passed and reinsert it back into its original parent. I would appreciate any help.
I can't use IDs. I can only uses classes.
HTML
<div class="main-div">
</div>
<div class="container">
<div class="contents">
A
</div>
</div>
<div class="container">
<div class="contents">
B
</div>
</div>
CSS
.main-div {
width: 100wv;
height: 200px;
background: yellow;
cursor: pointer;
}
.container {
width: 40vw;
height: 150px;
border: 2px solid purple;
display: inline-block;
}
.contents {
width: 50px;
height: 50px;
background: green;
}
JS
$('.container').click(function() {
var child = $(this).children();
console.log('child ' + child);
$('.main-div').append(child);
});
$('.main-div').click(function(child) {
console.log('child ' + child);
$('.main-div').detach(child);
});
FIDDLE
Set ids for the containers
<div class="main-div">
</div>
<div class="container" id="container1">
<div class="contents">
A
</div>
</div>
<div class="container" id="container2">
<div class="contents">
B
</div>
</div>
And set a data attribute for the children on click to identify the parent element.
$(function() {
$('.container').click(function() {
var child = $(this).children();
child.attr("data-parentcontainer", this.id);
$('.main-div').append(child);
});
$('.main-div').click(function(child) {
var child = $(this).children();
child.appendTo($("#" + child.data("parentcontainer")));
});
});
JSFIDDLE
Use this JS snippet and let me know if it helps
$('.container').click(function() {
console.log('foo');
var child = $(this).html();
console.log(child);
$('.main-div').append(child);
});
$('.main-div').click(function() {
console.log('foo');
var main = $(this).html();
if(main.length != 0) {
$('.main-div').empty();
}
else
console.log('Main div is empty');
});
External DEMO
You can use .detach() and .appendTo(), but along with that you have to keep some identification to know from that .contents div was picked up. So I am making use of data-address attribute for the parent of picked .contents so as to attach it back there. See inline comments for detailed explanation on what will happen with the code.
$('.main-div').on('click', function(e) {
var elem = $(e.target); //capture click event on .main-div
if (elem.hasClass('contents')) { //check if click was on .contents div
var text = elem.text().trim(); //if yes then get its text
elem.detach();//detach the element
elem.appendTo($('div[data-address=' + text + ']')); //attach it based on attribute selector of jquery
}
});
$('.contents').on('click', function() {
var elem = $(this);//get the element
elem.closest('.container').attr('data-address', elem.text().trim())
//add or update data-address attribute of its closest parent i.e. .container
elem.detach();//detach the element
elem.appendTo($('.main-div')); //append it to .main-div
})
.main-div {
width: 200px;
height: 200px;
background: yellow;
cursor: pointer;
}
.container {
width: 150px;
height: 150px;
border: 2px solid purple;
}
.contents {
width: 50px;
height: 50px;
background: green;
position: relative;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="main-div">
</div>
<div class="container">
<div class="contents">
A
</div>
</div>
<div class="container">
<div class="contents">
B
</div>
</div>
You need to add some uniqueness on the div that have class container, you can add a class or id so that when reinserting back to the original div we can identify the div.
i don't think is there other solution to reinsert back to the original div, whenever we identify both div uniquely.
please change your html structure so that we can manipulate through jquery.

Moving elements between 2 boxes

I have looked through same questions on this topic but somehow suggested solutions do not work for me :/
Problem is that divs get moved from #box1 to #box2 only once. If detach() used then divs are clickable in #box2 but get rearranged when clicked. If remove()used divs are not clickable in #box2 at all (event listener gone?). I have a feeling that the process of moving the divs is somehow not really complete and I ether have duplicates around in DOM or moved divs disappear entirely and do not react to clicks.
I tried detach(), remove() and appendTo() in various combinations and the best I can get is in the fiddle below
http://jsfiddle.net/uoz3t914/13/
$('#box1 .item' ).on('click', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).remove().appendTo('#box2');
});
$('#box2 .item' ).on('click', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).appendTo('#box1');
});
In your case you have to use the Event Delegation
$('#box1' ).off().on('click','.item', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).appendTo('#box2');
});
$('#box2' ).off().on('click', '.item', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).appendTo('#box1');
});
You attach the event to the parent, that propagate it to the children, and then any time that you attach the event, put an off() to detach it.
$('#box1' ).off().on('click','.item', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).appendTo('#box2');
});
$('#box2' ).off().on('click', '.item', function() {
// $( this ).detach().appendTo('#box2'); moves divs around in #box2
$( this ).appendTo('#box1');
});
.item {
width: 50px;
height: 50px;
float: left;
}
#box1 {
border: 1px dotted blue;
position: relative;
width: 200px;
height: 100px;
}
#i1 {
background-color: yellow;
}
#i2 {
background-color: green;
}
#i3 {
background-color: red;
}
#box2{
border: 1px solid black;
width: 250px;
height: 100px;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id ="box1">
<div class ="item" id ="i1"></div>
<div class ="item" id ="i2"></div>
<div class ="item" id ="i3"></div>
</div>
<div id = "box2">
</div>
You can move them between the boxes with:
$('#box1, #box2').on('click', '.item', function () {
$(this).appendTo($(this).parent().prop('id') == 'box1' ? '#box2' : '#box1');
});
$('#box1, #box2').on('click', '.item', function () {
$(this).appendTo($(this).parent().prop('id') == 'box1' ? '#box2' : '#box1');
});
.item {
width: 50px;
height: 50px;
float: left;
}
#box1 {
border: 1px dotted blue;
position: relative;
width: 200px;
height: 100px;
}
#i1 {
background-color: yellow;
}
#i2 {
background-color: green;
}
#i3 {
background-color: red;
}
#box2 {
border: 1px solid black;
width: 250px;
height: 100px;
position: relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="box1">
<div class="item" id="i1"></div>
<div class="item" id="i2"></div>
<div class="item" id="i3"></div>
</div>
<div id="box2"></div>
This uses .on()'s event delegation syntax to handle the elements, and a ternary operator to determine which box the element exists in.
Use this html:
<div id="wrapper">
<div id ="box1" class="container">
<div class ="item" id ="i1"></div>
<div class ="item" id ="i2"></div>
<div class ="item" id ="i3"></div>
</div>
<div id = "box2" class="container">
</div>
</div>
and this javascript
$('.item').on('click', function(){
var index = $("#wrapper > .container").index($(this).parent()),
maxIndex = $('#wrapper > .container').length,
nextIndex = (index + 1) < maxIndex ? (index + 1) : 0;
$(this).appendTo($('#wrapper > .container').eq(nextIndex));
});
in your fiddle to move boxes between any number of containers
You may also add Box3, Box4 (with class .container) etc. into the "#wrapper div", you may do it dynamycally

jquery-ui : drop&drag inventory with stackable items

Hello everyone,
I'm building a drop&drag inventory panel for my webgame, but I was unable to make it work with stackable elements. I have simplified the whole inventory so that it's less confusing.
FIrst off, let me explain how do I expect it to work:
Every .item element can be dropped on any free .inv_slot.
If I try to drop an .item element on another .item that does not contain class .stackable, it will simply activate the draggable's revert() function.
if I try to drop the .item element on an .item that does have the .stackable class,
it will only remove the clone/helper. (Later on I will addd an function that only increases the items stack size.)
Now what's wrong with the below example :
in case an .item accidentally dropped on border or between two .inv_slotslots, the Revert animation is not activated. It does work however, while dropping the .item element outside the #panel.
Also if I accidentally dropped an .item between two .inv_slot elements, it will behave as if the .item was dropped on a .stackable item. So it will remove the clone instead of reverting back to it's prev. position. (Most likely an issue with the selector in drop: method)
If I drop a .stackable item over another .stackable item, it does not refresh the cursor. It seems to be stuck in the drag mode which activates the "pointer" cursor.
Now here's the (partialy working) example:
$(document).ready(function() {
//var prev_el = '';
var item_isStackable = "";
$( ".item").draggable({
scroll: true,
revert: function(isValidEl)
{
if(isValidEl)
{
return false;
}else{
return true;
}
},
helper: "clone",
cursor: "pointer",
stack: false,
zIndex: 27,
drag: function(event, ui)
{
item_isStackable = $(this).hasClass("stackable");
},
});
$( ".inv_slot" ).droppable({
accept: ".item",
drop: function( event, ui ) {
var item = $(this).find(".item");
if(item.length == 0) /// See if there any items already in the currently selected inventory slot //
{
console.log("Inserting");
ui.draggable.detach().appendTo($(this)); // if none, insert the item into athe free slot ///
}
else if(item_isStackable == true && item.hasClass("stackable")){
console.log("Increasing ");
ui.draggable.detach(); /// If yes, just destroy the clone ///
}else{
console.log("reverting back");
// in case it's not .inv_slot , revert the item back to it's previous position //
ui.draggable.animate(ui.draggable.data().origPosition,"slow");
}
}
});
});
#panel
{
width: 340px;
height: 44px;
border: 1px solid #000;
padding: 4px;
}
.inv_slot
{
z-index: 22;
position: relative;
width: 40px;
height: 40px;
border: 1px solid red;
float: left;
}
.inv_slot .slot_pos{
z-index: 24;
position: absolute;
margin-left: 50%;
left: -4px; top: 2px;
}
.item
{
position: relative;
z-index: 25;
margin: 4px;
width: 30px;
height: 30px;
border: 1px solid blue;
}
.item.stackable
{
border: 1px solid green;
}
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.4/jquery-ui.js"></script>
<div id="panel">
<div class="inv_slot">
<div class="item stackable" ></div>
<span class="slot_pos">0</span>
</div>
<div class="inv_slot">
<div class="item"> </div>
<span class="slot_pos">1</span>
</div>
<div class="inv_slot">
<div class="item stackable"> </div>
<span class="slot_pos">2</span>
</div>
<div class="inv_slot"><span class="slot_pos">3</span> </div>
<div class="inv_slot"><span class="slot_pos">4</span> </div>
<div class="inv_slot"><span class="slot_pos">5</span> </div>
<div class="inv_slot"><span class="slot_pos">6</span> </div>
<div class="inv_slot"><span class="slot_pos">7</span> </div>
</div>
I have spent couple of hours without any progress , so I'd really appreciate if someone could help me out with this one.
Thanks in advance,
Alex.
What's happening is that when you drag a box onto a border next to it, it deletes itself because it tries to stack itself. You need to change the second part of your if statement:
else if(item_isStackable == true && item.hasClass("stackable") && ui.draggable.filter(function() { var d = this; return item.filter(function() { return d == this; }).length > 0; }).length === 0)
To fix the cursor pointer problem:
.item
{
position: relative;
z-index: 25;
margin: 4px;
width: 30px;
height: 30px;
border: 1px solid blue;
cursor: default !important; /* Add this property. */
}
Fiddle.

JQueryUI window not covering all content elements

I have a task at work where I must implement a simple browser-based chat system for an intranet. Backend is ready, and now I'm trying to create the UI part but it's giving me a big headache, and been banging my head against the desk for hours because of this :(
Here's my JSFiddle: http://jsfiddle.net/2PMvM/ (it uses JQuery + JQueryUI - 1.x)
The HTML, semantically, looks right, but it doesn't work as good as it looks. The idea is that the window (red border div) should contain all of its elements. I don't know why the list's horizontal scrollbar and then my textarea get out of the window, as if there's some offset I'm not taking into account?
I'm trying to do this with the least JS possible, but if the CSS way is too complicated, I'm willing to take a JS solution then.
What could be wrong though?. Thanks in advance :)
--- SO requires me to put the code, so here it goes:
Note: be sure to include this JS: https://github.com/AndrewDryga/jQuery.Textarea.Autoresize/raw/master/js/jquery.textarea.autoresize.js because it is required so my textarea autosizes when it gets more text. The window should contain the textarea even if it resizes. Seems like CSS could do it, but no?
IT IS SOLVED!! Thanks to #Trevor: Here's the updated code + Fiddle HERE. Thanks!
HTML:
<div class="window">
<div style="height: 20px;" class="wndHandle">Some username :D</div>
<div class="content" id="chatDiv" style="background:#fff;overflow: scroll;">
<ul class="ulChat">
<li class="msg in">Test message for testing 1</li>
<li class="msg out">Test message for testing 2</li>
<li class="msg out">Test message for testing 3</li>
<li class="msg in">Test message for testing 4</li>
</ul>
</div>
<div class="footer">
<textarea style="width: 100%; resize: none; overflow-x: hidden;" rows="1" cols="1"></textarea>
</div>
</div>
CSS:
.ui-resizable-handle
{
background: #f5dc58; border: 1px solid #FFF;
width: 9px; height: 9px;
z-index: 2;
}
.ui-resizable-se { right: -5px; bottom: -5px; }
.window
{
position: absolute;
display: inline-block;
width: 150px;
height: 200px;
border: 1px solid #bb0000;
}
.wndHandle {
cursor: move;
font-size: 9pt;
background: #888;
color: #fff;
padding-left: 1px;
}
Javascript:
var refresh = function() {
var objWindow = $('.window');
var objHandle = objWindow.find('.wndHandle');
var objContent = objWindow.find('.content');
var objFooter = objWindow.find('.footer');
var objResize = objWindow.find(".ui-resizable-handle.ui-resizable-se");
objResize.removeClass("ui-resizable-autohide");
objContent.height(
objWindow.innerHeight() -
objHandle.outerHeight() -
(
(objFooter.length == 0) ?
(objResize.length == 0) ? 0 : objResize.outerHeight()
: objFooter.outerHeight()
)
);
}
$('.window').draggable({ handle: '.wndHandle' }).resizable({
handles: 'se',
minWidth: 150,
minHeight: 100,
resize: function( event, ui ) {
refresh();
}
});
$('textarea').autoresize({
maxHeight: 80,
onResize: function () {
refresh();
}
});
refresh();
Here is a solution using jQuery to adjust the height
HTML - added ID
...
<div id="chatDiv" style="background:#fff;overflow: scroll;">
...
jQuery
$('.window').draggable({ handle: '.wndHandle' }).resizable({
handles: 'se',
minWidth: 150,
minHeight: 100,
resize: function( event, ui ) {
$('#chatDiv').height($('.window').height()-38);
}
});
$('#chatDiv').height($('.window').height()-38);
$('textarea').autoresize();
Fiddle

Categories

Resources