ORIGINAL QUESTION
I've got a draggable element (for the sake of clarity, let's call it $("#draggable")) that triggers a checkbox ($('#checkable')). When $('#draggable') is dragged to a box ($('#droppable')), it checks $('#checkable'), and when $('#draggable') is dragged out of $('#droppable'), it unchecks $('#checkable').
The first time I drag $("#draggable") to $('#droppable'), it checks $('#checkable') perfectly. When I drag $("#draggable") out of $('#droppable'), it unchecks $('#checkable') all fine. But when I redrag $("#draggable") to $('#droppable'), it does not check $('#checkable').
Here's my jQuery code:
$(document).ready(function(e) {
$('.draggable').draggable({ revert: "invalid", opacity: 0.7, helper: "clone" });
$('.droparea').droppable({
accept: function(el) {
if(el.hasClass($(this).data('accept'))) {
return true;
}
},
hoverClass: 'hover',
drop: function(e,ui) {
var $this = $(this);
if(app.is_empty($this.data('count')) || parseInt($this.data('count')) == 0) {
ui.draggable.appendTo($this);
$('#' + ui.draggable.data('rel')).attr('checked','checked');
console.log('#' + ui.draggable.data('rel'),$('#' + ui.draggable.data('rel')).is(':checked'));
} else {
var holder = $('.' + $this.data('accept') + 's');
var child = $this.children('.' + $this.data('accept'));
$('#' + child.data('rel')).attr('checked',null);
child.appendTo(holder);
ui.draggable.appendTo($this);
$('#' + ui.draggable.data('rel')).attr('checked','checked');
}
}
});
$('.holder').droppable({
accept: function(el) {
if(el.hasClass($(this).data('accept'))) {
return true;
}
},
drop: function(e,ui) {
var $this = $(this);
ui.draggable.appendTo($this);
$('#' + ui.draggable.data('rel')).attr('checked',null);
console.log('#' + ui.draggable.data('rel'),$('#' + ui.draggable.data('rel')).is(':checked'));
}
});
});
And here's my HTML:
<div class="drop-holder">
<div class="droparea addressbooks-drop ui-droppable" data-accept="addressbook">
<div class="text-placeholder">
Addressbooks
</div>
</div>
<div class="holder addressbooks ui-droppable" data-accept="addressbook">
<div class="draggable addressbook btn btn-primary ui-draggable ui-draggable-handle" data-rel="4685f5c2-473f-11e5-ad20-000c29b8330c">
Another Book (2124)
</div>
<div class="checkbox-holder">
Another Book (2124)
<input type="checkbox" value="4685f5c2-473f-11e5-ad20-000c29b8330c" id="4685f5c2-473f-11e5-ad20-000c29b8330c">
</div>
<div class="draggable addressbook btn btn-primary ui-draggable ui-draggable-handle" data-rel="04b506c5-4646-11e5-ad20-000c29b8330c">
default (4)
</div>
<div class="checkbox-holder">
default (4)
<input type="checkbox" value="04b506c5-4646-11e5-ad20-000c29b8330c" id="04b506c5-4646-11e5-ad20-000c29b8330c"></div>
</div>
</div>
Any reason why $('#checkable') isn't being checked the second time I drag $('#draggable') to $('#droppable')?
EDIT 1
jsFiddle as per Alex's request. :)
I switched .attr() with .prop() and replaced null with false, and it seems to work.
.prop('checked',true)
Have a look at prop and read when its better to use .prop() than .attr().
As it says in the docs:
Nevertheless, the most important concept to remember about the checked
attribute is that it does not correspond to the checked property. The
attribute actually corresponds to the defaultChecked property and
should be used only to set the initial value of the checkbox. The
checked attribute value does not change with the state of the
checkbox, while the checked property does. Therefore, the
cross-browser-compatible way to determine if a checkbox is checked is
to use the property:
if ( elem.checked )
if ( $( elem ).prop( "checked" ) )
if ( $( elem ).is( ":checked" ) )
Related
I am trying to implement a behavior for my draggable items.
The behavior is described here :
My box contains multiple draggable items which are sorted on page load
I can drag and item from that box on a drop area
But if I drag it back to the box it should re-place at its original position based on its data-attribute
I have no clue how to achieve this. I saw that sortable could possibly do this but I don't know how to combine it with draggable.
Thank you
HTML
<div class="multiple-drag-area position-sticky sticky-top">
<div class="box" data-position="1">
Item 1
</div>
<div class="box" data-position="2">
Item 2
</div>
<div class="box" data-position="3">
Item 3
</div>
</div>
<div class="drag-area">
Drop 1
</div>
<div class="drag-area>
Drop 2
</div>
<div class="drag-area>
Drop 3
</div>
JS
$( ".box" ).draggable({
scope: 'demoBox',
revert: true,
cursorAt: { top: 40, left: 40 },
revertDuration: 100,
start: function( event, ui ) {
//Reset
$( ".box" ).draggable( "option", "revert", true );
console.log('-');
}
});
$( ".multiple-drag-area" ).droppable({
scope: 'demoBox',
drop: function( event, ui ) {
var area = $(this).find(".area").html();
var box = $(ui.draggable).html()
$( ".box" ).draggable( "option", "revert", false );
//Display action in text
console.log("[Action] <b>" + box + "</b>" + " dropped on " + "<b>" + area + "</b>");
//Realign item
$(ui.draggable).detach().css({top: 0,left: 0, marginRight:4}).appendTo(this);
},
})
$( ".drag-area" ).droppable({
scope: 'demoBox',
drop: function( event, ui ) {
var area = $(this).find(".area").html();
var box = $(ui.draggable).html()
$( ".box" ).draggable( "option", "revert", false );
//Display action in text
console.log("[Action] <b>" + box + "</b>" + " dropped on " + "<b>" + area + "</b>");
//Realign item
$(ui.draggable).detach().css({top: 0,left: 0}).appendTo(this);
},
accept: function(draggable) {
return $(this).find("*").length-1 == 0 && (!$(this).hasClass("blocked-seat"));
}
})
Something like that should work:
$( ".box" ).draggable({
revert: true,
});
$( ".drag-area" ).droppable({
drop: function( event, ui ) {
$(ui.draggable).detach().css({top: 0,left: 0}).appendTo(this);
}
});
$( ".multiple-drag-area" ).droppable({
drop: function( event, ui ) {
let box = $(ui.draggable);
// check if Element gets reinserted
// we can check that by confirming the the box parent has the 'drag-area' class
if($(box).parent().hasClass("drag-area")){
$(box).detach().css({top: 0,left: 0}).appendTo(this);
// sort the boxes based on 'data-position'
$(this).find('.box').sort(function( a, b ) {
return a.dataset.position - b.dataset.position;
}).appendTo(this);
}
}
});
I'm trying to make an exam application which has drag & drop question type that the user might dragging correct elements to correct droppable boxes. Totally, I need get dropped elements/items id's as array or another and insert to mysql with php. I did something are below:
Javascript
$(".dragging-items .draggable-item").draggable({
helper: "clone",
opacity: 0.5,
scroll:true,
refreshPositions: true,
start: function( event, ui ) {
(this).addClass("dragging");
},
stop: function( event, ui ) {
$(this).removeClass("dragging");
},
});
$(".dragging-items").droppable({
hoverClass: "drop-hover",
drop:function(event, ui) {
ui.draggable.detach().appendTo($(this));
}
});
$(".droppable-list").droppable({
drop:function(event, ui) {
ui.draggable.detach().appendTo($(this));
var drag_id = $(ui.draggable).attr("id");
var drop_id = event.target.id;
var $tbox = $("input[name=droppable-list-"+ drop_id +"]");
var current_data = $tbox.val();
var new_data = current_data + drag_id + ", ";
$tbox.val(new_data);
},
});
HTML
<ul class="dragging-items">
<li class="draggable-item" id="1"> Item-1 </li>
<li class="draggable-item" id="2"> Item-2 </li>
<li class="draggable-item" id="3"> Item-</li>
</ul>
<div class="drag-drop">
<div class="droppable-title"> Box 1 </div>
<div class="droppable-item">
<div class="droppable-list" id="1"></div>
</div>
<input type="text" class="droppable-input" name="droppable-list-1">
</div>
<div class="droppable-title"> Box 2 </div>
<div class="droppable-item">
<div class="droppable-list" id="2"></div>
</div>
<input type="text" class="droppable-input" name="droppable-list-2">
</div>
</div>
JsFiddle
https://jsfiddle.net/7e568v2y/
I found a number of oddities in CSS that may want to be corrected. A lot of missed ;, but that's not the main focus. Also, you have a number of repeated ID attributes and these must be unique in HTML. Consider the following code.
$(function() {
function dropToArray(dropObj) {
var results = [];
$(".draggable-item", dropObj).each(function(i, el) {
results.push($(el).data("drag-id"));
});
return results;
}
function updateDropList(dropObj) {
var dropped = dropToArray(dropObj);
var $tbox = $("input[name=droppable-list-" + dropObj.attr("id") + "]");
$tbox.val(dropped.join(","));
}
$(".dragging-items .draggable-item").draggable({
helper: "clone",
opacity: 0.5,
scroll: true,
refreshPositions: true,
start: function(event, ui) {
$(this).addClass("dragging");
},
stop: function(event, ui) {
$(this).removeClass("dragging");
},
});
$(".dragging-items").droppable({
hoverClass: "drop-hover",
drop: function(event, ui) {
ui.draggable.detach().appendTo($(this));
}
});
$(".droppable-list").droppable({
drop: function(event, ui) {
var $this = $(this);
if ($this.find(".droppable-list").length >= 2) {
ui.draggable.draggable("option", "revert", true);
return;
} else {
ui.draggable.detach().appendTo($(this));
setInterval(function() {
$(".droppable-list").each(function(i, el) {
updateDropList($(el));
});
}, 100);
}
},
});
});
Always best to create a few simple functions if you're going to be doing something often. I created two, one to iterate the container and build an array of the specific ID. To help ensure the same data comes back, I added the data-drag-id attribute. Now we can have unique IDs and still retain the ID Number for each item.
Working example: https://jsfiddle.net/Twisty/t1a40Lsn/22/
As you can see, when an item is dragged and dropped, it updates the field for all the lists. This addresses an issue if the user moves it from one box to another. Since the data is in an Array, we can simple use .join() to make a nice well formatted string of data.
I'm running the following html:
<div class="container">
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<div id="container1">
<div class="resizeDiv col-6"></div>
<div class="resizeDiv col-6"></div>
</div>
</div>
<div class="col">
<div id="container2"></div>
</div>
</div>
</div>
</div>
</div>
And this is the JS
var currentParent;
$(".resizeDiv").draggable({
revert: 'invalid',
containment: "#container2",
start: function(){
currentParent = $(this).parent().attr('id');
}
});
$('#container1, #container2').droppable({
accept:'.resizeDiv',
drop: function(event,ui) {
if (currentParent != $(this).attr('id')) {
$(ui.draggable).appendTo($(this)).removeAttr('style');
}
$(this).find("div").on("click", function(){
if($(this).hasClass("col-6")) {
$(this).removeClass("col-6").addClass("col");
} else {
$(this).removeClass("col").addClass("col-6");
}
});
}
});
When I drop one element and I click on it, its class changes from col-6 to col, however when I drop the second item, the toggle classes on the click only works on the second added item and not the first one added.
Here it is a codepen, try adding one item to the second container and the click on it and then drop the second item and try to click on them.
NOTE
Ideally I would like to auto size without the click
Your problem is this
$(this).find("div").on("click", function(){
if($(this).hasClass("col-6")) {
$(this).removeClass("col-6").addClass("col");
} else {
$(this).removeClass("col").addClass("col-6");
}
});
What this does is every time you drop an element it is going to go through ALL divs in that container. That means the ones you already set a click handler on, so when you drop a second item, the first div is going to get a second click handler. If you dropped a third it would get a third and so on.
So when you click the first one you are actually firing off two of these callbacks, and the class gets added in the first and removed in the second.
Since ui.draggable refers to the dragged element use that as a reference to add the click handler
$(ui.draggable).on("click",function(){
let $this = $(this);
if($this.hasClass("col-6")) {
$this.removeClass("col-6").addClass("col");
} else {
$this.removeClass("col").addClass("col-6");
}
//note instead of doing the if else and removeClass().addClass()
//calls you could just do toggleClass('col-6 col')
//it will toggle one off and the other on
$this.toggleClass('col-6 col');
});
If instead you want to get rid of the click handler and just want to add the class directly then do so on ui.draggable
$this = $(this);
$draggable = $(ui.draggable);
if (currentParent != $this.attr('id')) {
$draggable.appendTo($this).removeAttr('style').toggleClass('col-6 col');
}
Also note I am storing the jQuery objects. If you are going to use something like $(this) or $(ui.draggable) multiple times it is better to store it in a variable and use it instead of making multiple calls to jQuery()
The solution I found was to use:
e.stopImmediatePropagation();
Link to doc
Final js code
var currentParent;
$(".resizeDiv").draggable({
revert: 'invalid',
containment: "#container2",
start: function(){
currentParent = $(this).parent().attr('id');
}
});
$('#container1, #container2').droppable({
accept:'.resizeDiv',
drop: function(event,ui) {
if (currentParent != $(this).attr('id')) {
$(ui.draggable).appendTo($(this)).removeAttr('style');
}
$(this).find("div").on("click", function(e){
e.stopImmediatePropagation();
if($(this).hasClass("col-6")) {
$(this).css("background-color", "red");
$(this).removeClass("col-6").addClass("col");
} else {
$(this).css("background-color", "yellow");
$(this).removeClass("col").addClass("col-6");
}
});
}
});
NOTE
Yet I am still trying to auto size if el is dropped next to the other.
I'm sorry for my bad English but I try to make the question understandable.
In the snippet (Or the jsfiddle) We can find two draggable items:
foo (If you drag it and drop in the Div content editable the [% foo %] text appear where we drop it between the text)
foo2 (This have the Jquery UI drag style (it follows the cursor))
I want to achieve the functionalitty of foo With jquery UI style. But when I try : event.dataTransfer.setData("text/plain", "[%" + event.srcElement.innerHTML + "%]"); It looks like event is not define if I try to define the function like this:
drag: function(ui, event)
The motion of the foo2 Stop work (It mean is a error in the code)
Here is the snippet:
//$( "#foo2" ).draggable();
$( "#foo2" ).draggable({
start: function(ui) {
event.preventDefault();
},
drag: function(ui) {
},
stop: function(ui) {
}
});
var btn = document.getElementById("foo");
btn.onclick = function(event) {
event.preventDefault();
};
btn.ondragstart = function(event) {
event.dataTransfer.setData("text/plain", "[%" + event.srcElement.innerHTML + "%]");
};
<span draggable="true" id="foo" class="btn btn-primary">Foo</span>
<div style="background:red" contenteditable="true" >Put foo between this and this</div>
<a id="foo2" class="btn btn-primary">Foo2</a>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
The same demo: http://jsfiddle.net/fbprfj5c/1/
Thanks for all your help, recommendations or tips :)
Update:
In response to Gaetano:
I like your idea, and is my fault for no tell all the details. You get the main idea but the tricky way of get the the Width of character and calculate the position with that i can do a similar function to achieve the same effect with a more larger div (in the y axis). But in the div can be more elements than characters. I"ll start with your idea, But i think is a shame open another question for this. In another hand you resolve the problem But that "hacky"/simulation to get the caret position i try to improve (I try before without achieve it), So let"s wait a little is someone have an idea
You may use Droppable for the div element and connect your foo2 draggable element to the droppable.
$('div[contenteditable="true"]').droppable({
drop: function( e, ui ) { // 6.413793103448276
var charWidth = getCharWidth(this);
var position = Math.round(ui.offset.left / charWidth);
position = (position - 1 >= 0) ? position - 1 : position;
$(this).text(function(idx, txt) {
return txt.substr(0, position) + "[%" + ui.draggable.text() + "%]" + txt.substr(position + 1);
})
}
});
$( "#foo2" ).draggable({
connectToSortable: 'div[contenteditable="true"]',
revert: "invalid",
helper: "clone"
});
var btn = document.getElementById("foo");
btn.onclick = function(event) {
event.preventDefault();
};
btn.ondragstart = function(event) {
event.dataTransfer.setData("text/plain", "[%" + event.srcElement.innerHTML + "%]");
};
function getCharWidth(ele) {
var f = window.getComputedStyle(ele)['font'],
o = $('<div>' + ele.textContent + '</div>')
.css({'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font': f})
.appendTo($('body')),
w = o.width() / ele.textContent.length;
o.remove();
return w;
}
<link href="https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<span draggable="true" id="foo" class="btn btn-primary">Foo</span>
<div style="background:red" contenteditable="true" >Put foo between this and this</div>
<a id="foo2" class="btn btn-primary">Foo2</a>
Hi all how can i get the Id of on dragged element and bind the id where i am dropping the element
here is what i am doing for my drag and drop
var i = 1;
var z = 1;
$(document).ready(function () {
$("button[id='columnadd']").click(function () {
// create the element
var domElement = $('<div id="shoppingCart' + i++ + '" class="shoppingCart"><h2 class="ui-widget-header">Add Menu Items Here <img src="../Images/delete.png" id="shoppingCart' + z++ + '" style="float: right; cursor:pointer;" onclick="removecolumn(this.id)"/></h2><aside class="ui-widget-content" id="droppable"><ol><li class="placeholder">Add your items here</li></ol></aside></div>');
var holder = "#columnholder";
$("#columnholder").append(domElement);
//$(this).after(domElement);
// at this point you have domElement in your page
// make it droppable
domElement.find("ol").droppable({
activeClass: "ui-state-default",
accept: '.small_box li',
greedy: true,
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
});
function makeItDroppableToo(e, ui, obj) {
$(obj).find(".placeholder").remove();
var id = this.id;
alert(id);
var placeholder = $("<ol><li>Add Sub Menu here</li></ol>");
$("<li></li>").append('<a draggable="true" droppable=true>' + ui.draggable.text() + '</a>').append(placeholder).appendTo($(obj));
// this is the same as the previous one
placeholder.droppable({
greedy: true,
// put options here
drop: function (event, ui) {
makeItDroppableToo(event, ui, this);
}
});
}
$(".small_box li" ).draggable({
appendTo: "body",
helper: "clone"
});
});
and this i my html from where i am dragging
<div class="top-header"><span class="heading">Menu Builder</span><button id="preview" onclick="savemenu()" >Save</button><button id="columnadd" >Add Column</button></div>
<div id="columnholder" class="column-holder"></div>
<div class="clear"></div>
<div class="menu-items">
<aside class="small_box">
<div class="menu-item-header"><h4>BRANDS</h4></div>
<ul>
<li id ="1"><a id ="1001" class="" href="#">Brand1</a></li>
<li id ="2"><a id ="1002" href="#">Brand2</a></li>
<li id ="3"><a id ="1003" href="#">Brand3</a></li>
<li id ="4"><a id ="1004" href="#">Brand4</a></li>
<li id ="5"><a id ="1005" class="" href="#">Brand5</a></li>
<li id ="6"><a id ="1006" href="#">Brand6</a></li>
<li id ="7"><a id ="1007" href="#">Brand7</a></li>
<li id ="8"><a id ="1008" href="#">Brand8</a></li>
</ul>
</aside>
here i will drag the element when i do that i want to get the id and when i am bind the dragged element here
$("<li></li>").append('<a draggable="true" droppable=true>' + ui.draggable.text() + '</a>').append(placeholder).appendTo($(obj));
i want to bind that id to the ,can any one tell me how can i do this
Inside the drop handler, you can use ui.draggable.attr('id') to get the id of the dragged element.
i.e.
...
drop: function(ev, ui) {
/*
// get id of dragged element
var draggedID = ui.draggable.attr('id');
// bind it
#('#'+draggedID).bind(...); // bind your events here
*/
// why get the id, when you can bind it directly
// will work with elements without an id too (credit to Rory McCrossan)
ui.draggable.bind(...);
},
...