Drag-and-Drop with jQuery: Your Essential Guide

  You are currently not logged in. You can view the forums, but cannot post messages. Log In | Register

17-Feb-11 00:00
This is a forum topic for discussing the article "Drag-and-Drop with jQuery: Your Essential Guide":

http://www.elated.com/articles/drag-and-drop-with-jquery-your-essential-guide/

Learn how to use jQuery, and the jQuery UI Draggable and Droppable plugins, to create drag-and-drop interfaces in your web pages. Includes a full drag-and-drop card game example.
18-Feb-11 09:26
Hi, thanks for the effort, but... whats new and relevant in this post, that is not here: http://jqueryui.com/?
18-Feb-11 17:54
@Paul11: The jQuery UI site is great for reference, but it doesn't have an introductory tutorial for drag-and-drop AFAIK. Hopefully my tutorial fills this gap to some extent.

--
Matt Doyle, Elated
04-Mar-11 02:54
Great tutorial! Will be a base for a bar custom plugin. Nice!!

--
by Decret
04-Mar-11 16:31
@Decret: Thanks - glad you enjoyed it.

--
Matt Doyle, Elated
02-Apr-11 20:54
Thanks for the tutorial.

It helps me trying to implement a calendar system where people can drag events type to fill a day.
I have a few questions though:
1. I have set helper:"clone" but when dragging the original is moved.
In my case a user can select the same event multiple time during the date so I need the draggable item to remain in place. I did this using the following code:


var clone = $(ui.draggable).clone();
$(this).append(clone);
clone.position( { of: $(this), my: 'left top', at: 'left top' } );
clone.removeClass('draggable')
clone.draggable({
snap: '.ui-droppable',

});



not sure if it's the best way but it seems it's working. Any better solution?

2. I would like if an item is dragged out of the droppable container (cardSlots in the example) the item being dragged to fade out (disappear).

3. Is it possible to have the same container draggable and droppable? I need to be able to move items around after they have been dropped with some rules enforced like after one item was dropped no other item can be dropped in the same location, but if it's moved the location is available again.

Thanks again for the great tutorial

Mike
04-Apr-11 05:55
Hi MikeQ,

Glad you found the tutorial helpful!

1. Not sure why the original would move if you used helper: 'clone'. That's certainly not the expected behaviour. Maybe you could post the URL of the full page and we can take a look at the problem?

2. Check out the 'out' event for Droppable: http://jqueryui.com/demos/droppable/ - you could create an event handler for this that calls fadeOut() on the draggable then removes it from the DOM.

3. I've no idea. Have you tried it? Also by default you can move a draggable again after it's been dragged on a droppable - it's only disabled in my card game demo because that's the way the game works.

--
Matt Doyle, Elated
04-Apr-11 20:44
Hi Matt,

This is what I have:



<!doctype html>
<html lang="en">
<head>

<title>A jQuery Drag-and-Drop Number Cards Game</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<link rel="stylesheet" type="text/css" href="style.css">

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.11/jquery-ui.min.js"></script>
<script type="text/javascript">

var correctCards = 0;
$( init );

function init() {

// Hide the success message
$('#successMessage').hide();
$('#successMessage').css( {
left: '580px',
top: '250px',
width: 0,
height: 0
} );

// Reset the game
correctCards = 0;
$('#cardPile').html( '' );
$('#cardSlots').html( '' );

// Create the pile of shuffled cards
var numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
numbers.sort( function() { return Math.random() - .5 } );

for ( var i=0; i<10; i++ ) {
$('<div>' + numbers[i] + '</div>').data( 'number', numbers[i] ).attr( 'id', 'card'+numbers[i] ).attr( 'sNumber', '' ).appendTo( '#cardPile' ).draggable( {
containment: '#content',
stack: '#cardPile div',
cursor: 'move',
revert: true,
helper: 'clone',
zIndex: '2700' ,
opacity: 0.5
} );
}

// Create the card slots
var words = [ 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten' ];
for ( var i=1; i<=10; i++ ) {
$('<div>' + words[i-1] + '</div>').data( 'number', i ).attr( 'sid', 'slot'+i ).appendTo( '#cardSlots' ).droppable( {
accept: '#cardPile div, #cardSlots div',
hoverClass: 'hovered',
drop: handleCardDrop


} );
}

}

function handleCardDrop( event, ui ) {

var slotNumber = $(this).data( 'number' );
var cardNumber = ui.draggable.data( 'number' );
var oldSlotNumber = ui.draggable.attr( 'sNumber' );
ui.draggable.attr( 'sNumber', slotNumber );

// If the card was dropped to the correct slot,
// change the card colour, position it directly
// on top of the slot, and prevent it being dragged
// again

//if ( slotNumber == cardNumber ) {
ui.draggable.addClass( 'correct' );
// ui.draggable.draggable( 'disable' );
if (oldSlotNumber == '')
$(this).droppable('disable');
else {
$("[sid=slot" + oldSlotNumber + "]").droppable({
disabled: false
});
ui.helper.remove();
}

var clone = $(ui.draggable).clone();
$(this).append(clone);
clone.position( { of: $(this), my: 'left top', at: 'left top' } );
clone.removeClass('draggable')
clone.draggable({
snap: '.ui-droppable'

});
ui.draggable.draggable( 'option', 'revert', false );
correctCards++;
//}

// If all the cards have been placed correctly then display a message
// and reset the cards for another go

if ( correctCards == 10 ) {
$('#successMessage').show();
$('#successMessage').animate( {
left: '380px',
top: '200px',
width: '400px',
height: '100px',
opacity: 1
} );
}

}

</script>

</head>
<body>

<div class="wideBox">
<h1>Drag-and-Drop with jQuery: Your Essential Guide</h1>
<h2>A Number Cards Game</h2>
</div>

<div id="content">

<div id="cardPile"> </div>
<div id="cardSlots"> </div>

<div id="successMessage">
<h2>You did it!</h2>
<button onclick="init()">Play Again</button>
</div>

</div>

<div class="wideBox">
<p>&copy; Elated.com | <a href="http://www.elated.com/articles/drag-and-drop-with-jquery-your-essential-guide/">Back to Tutorial</a></p>
<p style="font-size: .8em; line-height: 1.5em;"><a rel="license" href="http://creativecommons.org/licenses/by/3.0/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by/3.0/88x31.png" /></a><br />This <span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" rel="dc:type">work</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://www.elated.com/" property="cc:attributionName" rel="cc:attributionURL">http://www.elated.com/</a> is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/3.0/">Creative Commons Attribution 3.0 Unported License</a>.</p>
</div>

</body>
</html>



It's working, well almost, I still can't delete cards by dragging them out of the cardSlots div and I get an error on IE (all versions except 9).
This is the error:
SCRIPT5007: Unable to get value of the property 'options': object is null or undefined
jquery-ui.min.js, line 54 character 158

Thanks
Mike
05-Apr-11 22:01
@MikeQ:

I think you might want to record when a draggable is successfully dropped on a droppable - you could do this in handleCardDrop(), and store the "dropped" status in the draggable using data(). Then create a handleCardOut event handler that binds to the droppable's 'out' event. If the card in question's "dropped" status is true then fade the card out by calling fadeOut().

Quick thought - wouldn't it make more sense to create the clone when the user starts dragging the card (the draggable's 'start' event), rather than when the card is dropped (the droppable's 'drop' event)?

Not sure about the IE error - might be a jQuery UI issue?

--
Matt Doyle, Elated
08-Apr-11 18:53
Hi Matt, thanks for the tutorial, very interesting. I'm working on a page which includes drag and drop items with jquerys. Call me thick here but I've been trying to see the bit of code that relates to the controlling of the parameters of the drag and drop zone. I see in the demo that the parameters are nicely set and the cards cannot be dragged out of the zone. However, my drag and drop items can be dragged off the page to the right and bottom and I need to stop this.

Can you give me an indication of what coding I have to insert?

Many thanks

--
Ely
11-Apr-11 00:35
@Ely: The 'containment' option controls limits the draggable elements to a specific element or rectangle. See the "Adding draggable options" section of the tutorial.

--
Matt Doyle, Elated
11-Apr-11 03:18
Many thanks Matt, I went through this "Adding draggable options" tutorial, changed the code using containment: '#content', snap: '#content' and .......... my problem is solved!!

I've got another issue now. My Javascript is working perfectly in Firefox, Crome, Safari, IE 8 but not in IE 7. I haven't tried it in IE 6 but I imagine its not working either. I'm not able to select and drag my images. Any ideas?

Thanks again

Ely

--
Ely
12-Apr-11 02:06
@Ely: Any JavaScript errors in IE7?

--
Matt Doyle, Elated
12-Apr-11 04:28
Hi Matt, yes at the bottom left the warning 'error on page' appears when I place the mouse on top of any of the images. I haven't got this live yet, but here's the code (quite a lot I'm afraid):



//<!--Main.js Site-->
//Everything inside will load as soon as the DOM is loaded and before the page contents are loaded.

$(document).ready(function() {

//Let´s give our “ul”s a Carousel effect by adding the following line of code: $('.productsNav').jcarousel();

$('.productsNav').jcarousel();
$('.desc').css({'opacity':'0.8'})
//Let’s apply a hover effect on the product images so that we can see some info regarding the respective product
//This code will apply a nice hover “slideDown” and “slideUp” effect.
$('.productsNav li').hover(
function(){
$('.desc', this).slideDown()
},
function(){
$('.desc', this).slideUp()
}
)//End of hover

//Apply reflection to all images of class
$('.reflected').draggable({ revert: 'invalid', helper:'clone', opacity: 0.4, appendTo: 'body',zIndex:9999, containment: '#content', snap: '#content'}).reflect({opacity:0.3, height:0.2});

//calculates total price
var cartTotal = function(){
var total = 0;
$('.products').each( function() {
var price = parseFloat($('span.price', this).text());
var quantity = parseInt($('span.quantity', this).text());

total += price * quantity;
}
);
$('#showTotal').text(total);
}

//Now let´s bring the “draggable” and “droppable” of jquery UI in action
//Adds products to cart
var addProductToCart = function(event, dragged){

/*var $div = $('#container');
$('.jcarousel-item')
.jcarousel-item("start",function(event, dragged){
dragged.limit = $div.offset();
dragged.limit.bottom = dragged.limit.top + $div.outerHeight() - $( this ).outerHeight();
dragged.limit.right = dragged.limit.left + $div.outerWidth() - $( this ).outerWidth();
})
.jcarousel-item(function(event, dragged){
$( this ).css({
top: Math.min( dragged.limit.bottom, Math.max( dragged.limit.top, dragged.offsetY ) ),
left: Math.min( dragged.limit.right, Math.max( dragged.limit.left, dragged.offsetX ) )
});
});*/


//storing the dragged element in a variable
var pObj = $(dragged.draggable)
//storing the main parent i.e "li" in our case
var mainParent = pObj.parent().parent();
var productImage = pObj.attr('src');
var productId = pObj.attr('id').split('_');
var productTitle = mainParent.children('.desc').find('.details').text();
var productPrice = mainParent.children('.desc').find('.num').text();

//checking if that product already exist in cart, if so we will increase the quantity by 1
//else a product will get append to the dropOnMe div
var chkCart = $('#'+productId[1]+'_cart');

//=================================================================
//Conditions
//=================================================================



var chkCart2 = $('#'+'prod5'+'_cart');
var chkCart3 = $('#'+'prod6'+'_cart');

if((productId[1] == 'prod6') && (chkCart2.size()==1) ||
(productId[1] == 'prod5') && (chkCart3.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart2 = $('#'+'prod'+'_cart');
chkCart3 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart2.size()==1) ||
(productId[1] == 'prod') && (chkCart3.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}

//===============================================================

var chkCart4 = $('#'+'prod1'+'_cart');
var chkCart5 = $('#'+'prod2'+'_cart');

if((productId[1] == 'prod2') && (chkCart4.size()==1) ||
(productId[1] == 'prod1') && (chkCart5.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart4 = $('#'+'prod'+'_cart');
chkCart5 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart4.size()==1) ||
(productId[1] == 'prod') && (chkCart5.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}

//=================================================================

var chkCart6 = $('#'+'prod1'+'_cart');
var chkCart7 = $('#'+'prod4'+'_cart');

if((productId[1] == 'prod4') && (chkCart6.size()==1) ||
(productId[1] == 'prod1') && (chkCart7.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart6 = $('#'+'prod'+'_cart');
chkCart7 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart6.size()==1) ||
(productId[1] == 'prod') && (chkCart7.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart8 = $('#'+'prod2'+'_cart');
var chkCart9 = $('#'+'prod4'+'_cart');

if((productId[1] == 'prod4') && (chkCart8.size()==1) ||
(productId[1] == 'prod2') && (chkCart9.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart8 = $('#'+'prod'+'_cart');
chkCart9 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart8.size()==1) ||
(productId[1] == 'prod') && (chkCart9.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart10 = $('#'+'prod1'+'_cart');
var chkCart11 = $('#'+'prod3'+'_cart');

if((productId[1] == 'prod3') && (chkCart10.size()==1) ||
(productId[1] == 'prod1') && (chkCart11.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart10 = $('#'+'prod'+'_cart');
chkCart11 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart10.size()==1) ||
(productId[1] == 'prod') && (chkCart11.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart12 = $('#'+'prod3'+'_cart');
var chkCart13 = $('#'+'prod4'+'_cart');

if((productId[1] == 'prod4') && (chkCart12.size()==1) ||
(productId[1] == 'prod3') && (chkCart13.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart12 = $('#'+'prod'+'_cart');
chkCart13 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart12.size()==1) ||
(productId[1] == 'prod') && (chkCart13.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart14 = $('#'+'prod5'+'_cart');
var chkCart15 = $('#'+'prod7'+'_cart');

if((productId[1] == 'prod7') && (chkCart14.size()==1) ||
(productId[1] == 'prod5') && (chkCart15.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart14 = $('#'+'prod'+'_cart');
chkCart15 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart14.size()==1) ||
(productId[1] == 'prod') && (chkCart15.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart16 = $('#'+'prod6'+'_cart');
var chkCart17 = $('#'+'prod7'+'_cart');

if((productId[1] == 'prod7') && (chkCart16.size()==1) ||
(productId[1] == 'prod6') && (chkCart17.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart16 = $('#'+'prod'+'_cart');
chkCart17 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart16.size()==1) ||
(productId[1] == 'prod') && (chkCart17.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================
//=================================================================

var chkCart18 = $('#'+'prod7'+'_cart');
var chkCart19 = $('#'+'prod8'+'_cart');

if((productId[1] == 'prod8') && (chkCart18.size()==1) ||
(productId[1] == 'prod7') && (chkCart19.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart18 = $('#'+'prod'+'_cart');
chkCart19 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart18.size()==1) ||
(productId[1] == 'prod') && (chkCart19.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart20 = $('#'+'prod6'+'_cart');
var chkCart21 = $('#'+'prod8'+'_cart');

if((productId[1] == 'prod8') && (chkCart20.size()==1) ||
(productId[1] == 'prod6') && (chkCart21.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart20 = $('#'+'prod'+'_cart');
chkCart21 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart20.size()==1) ||
(productId[1] == 'prod') && (chkCart21.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart24 = $('#'+'prod9'+'_cart');
var chkCart25 = $('#'+'prod11'+'_cart');

if((productId[1] == 'prod11') && (chkCart24.size()==1) ||
(productId[1] == 'prod9') && (chkCart25.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart24 = $('#'+'prod'+'_cart');
chkCart25 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart24.size()==1) ||
(productId[1] == 'prod') && (chkCart25.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart22 = $('#'+'prod9'+'_cart');
var chkCart23 = $('#'+'prod10'+'_cart');

if((productId[1] == 'prod10') && (chkCart22.size()==1) ||
(productId[1] == 'prod9') && (chkCart23.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart22 = $('#'+'prod'+'_cart');
chkCart23 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart22.size()==1) ||
(productId[1] == 'prod') && (chkCart23.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart24 = $('#'+'prod9'+'_cart');
var chkCart25 = $('#'+'prod12'+'_cart');

if((productId[1] == 'prod12') && (chkCart24.size()==1) ||
(productId[1] == 'prod9') && (chkCart25.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart24 = $('#'+'prod'+'_cart');
chkCart25 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart24.size()==1) ||
(productId[1] == 'prod') && (chkCart25.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart26 = $('#'+'prod11'+'_cart');
var chkCart27 = $('#'+'prod12'+'_cart');

if((productId[1] == 'prod12') && (chkCart26.size()==1) ||
(productId[1] == 'prod11') && (chkCart27.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart26 = $('#'+'prod'+'_cart');
chkCart27 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart26.size()==1) ||
(productId[1] == 'prod') && (chkCart27.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart28 = $('#'+'prod11'+'_cart');
var chkCart29 = $('#'+'prod10'+'_cart');

if((productId[1] == 'prod10') && (chkCart28.size()==1) ||
(productId[1] == 'prod11') && (chkCart29.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart28 = $('#'+'prod'+'_cart');
chkCart29 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart28.size()==1) ||
(productId[1] == 'prod') && (chkCart29.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart30 = $('#'+'prod13'+'_cart');
var chkCart31 = $('#'+'prod14'+'_cart');

if((productId[1] == 'prod14') && (chkCart30.size()==1) ||
(productId[1] == 'prod13') && (chkCart31.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart30 = $('#'+'prod14'+'_cart');
chkCart31 = $('#'+'prod13'+'_cart');
if((productId[1] == 'prod13') && (chkCart30.size()==1) ||
(productId[1] == 'prod14') && (chkCart31.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart30 = $('#'+'prod14'+'_cart');
var chkCart31 = $('#'+'prod15'+'_cart');

if((productId[1] == 'prod15') && (chkCart30.size()==1) ||
(productId[1] == 'prod14') && (chkCart31.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart30 = $('#'+'prod'+'_cart');
chkCart31 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart30.size()==1) ||
(productId[1] == 'prod') && (chkCart31.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart32 = $('#'+'prod13'+'_cart');
var chkCart33 = $('#'+'prod15'+'_cart');

if((productId[1] == 'prod15') && (chkCart32.size()==1) ||
(productId[1] == 'prod13') && (chkCart33.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart32 = $('#'+'prod'+'_cart');
chkCart33 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart32.size()==1) ||
(productId[1] == 'prod') && (chkCart33.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart34 = $('#'+'prod14'+'_cart');
var chkCart35 = $('#'+'prod16'+'_cart');

if((productId[1] == 'prod16') && (chkCart34.size()==1) ||
(productId[1] == 'prod14') && (chkCart35.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart34 = $('#'+'prod'+'_cart');
chkCart35 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart34.size()==1) ||
(productId[1] == 'prod') && (chkCart35.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart36 = $('#'+'prod13'+'_cart');
var chkCart37 = $('#'+'prod16'+'_cart');

if((productId[1] == 'prod16') && (chkCart36.size()==1) ||
(productId[1] == 'prod13') && (chkCart37.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart36 = $('#'+'prod15'+'_cart');
chkCart37 = $('#'+'prod16'+'_cart');
if((productId[1] == 'prod16') && (chkCart36.size()==1) ||
(productId[1] == 'prod15') && (chkCart37.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart38 = $('#'+'prod2'+'_cart');
var chkCart39 = $('#'+'prod3'+'_cart');

if((productId[1] == 'prod3') && (chkCart38.size()==1) ||
(productId[1] == 'prod2') && (chkCart39.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart38 = $('#'+'prod5'+'_cart');
chkCart39 = $('#'+'prod8'+'_cart');
if((productId[1] == 'prod8') && (chkCart38.size()==1) ||
(productId[1] == 'prod5') && (chkCart39.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================

//=================================================================

var chkCart40 = $('#'+'prod10'+'_cart');
var chkCart41 = $('#'+'prod12'+'_cart');

if((productId[1] == 'prod12') && (chkCart40.size()==1) ||
(productId[1] == 'prod10') && (chkCart41.size()==1)){

alert ( "Pack2-Lite2 cannot be bought with Pack2-EXPRESS2." );
}
else{
chkCart40 = $('#'+'prod'+'_cart');
chkCart41 = $('#'+'prod'+'_cart');
if((productId[1] == 'prod') && (chkCart40.size()==1) ||
(productId[1] == 'prod') && (chkCart41.size()==1)){

alert ( "Pack2-ENTERPRISE2 cannot be bought with Pack3-Lite3." );
}


//==================================================================
//====================================================================================================
else{


if(chkCart.size()==1)
{
var quantity = parseInt(chkCart.find('.quantity').text())+1;
chkCart.find('.quantity').text(quantity)
}
else
{
$('#dropOnMe').append('<div class="products" id="'+productId[1]+'_cart"><div class="productImage"><img src="'+productImage+'" title="'+productTitle+'"/></div><div class="productInfo"><span class="name">'+productTitle+'</span> <span class="price">'+productPrice+'</span> (x<span class="quantity">1</span>)</div><div class="productRemove"><img src="images/remove.png" /></div></div><div class="clear"></div>')
.find('div.products:last')
.fadeIn(1000)
.find('.productRemove')
.click(function()
{

$(this).parent().fadeOut(1000, function()
{
var node = $(this)
node.remove();

//after removing of the product we will re-calulate the total price
cartTotal();
})

});

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}

}// Always remember to put a closed bracket after adding a condition
//calculates total price
cartTotal();

};

$('#dropOnMe').droppable({
accept:'.reflected',
activeClass:'activeClassCart',
hoverClass:'hoverClassCart',
drop:addProductToCart
});

})

//========================================================


var cartTotal2 = function(){

var total ="";
var totalPrice = 0;
$('.products').each( function() {
var price = parseFloat($('span.price', this).text());
var quantity = parseInt($('span.quantity', this).text());
total += $('span.name', this).text() + "\n" + " Price: "+ price + " Quantity: "+ quantity +"\n" ;
totalPrice += price * quantity;

}

);
total = total+ "Total Price = " + totalPrice;
$('#showTotal1').text(total);
return total ;
}


function sendRequest()
{
var cart = cartTotal2();
// alert(cart);
var eml = "rafa_sarmiento@yahoo.co.uk";
var bod ="&body=" + cart;
var subj ="?subject=Order";
location.href="mailto:"+eml+subj+bod;

}

//========================================================

//The Accordeon

var ContentHeight = 1570;
var TimeToSlide = 250.0;

var openAccordion = '';

function runAccordion(index)
{
var nID = "Accordion" + index + "Content";
if(openAccordion == nID)
nID = '';

setTimeout("animate(" + new Date().getTime() + "," + TimeToSlide + ",'"
+ openAccordion + "','" + nID + "')", 33);

openAccordion = nID;
}

function animate(lastTick, timeLeft, closingId, openingId)
{
var curTick = new Date().getTime();
var elapsedTicks = curTick - lastTick;

var opening = (openingId == '') ? null : document.getElementById(openingId);
var closing = (closingId == '') ? null : document.getElementById(closingId);

if(timeLeft <= elapsedTicks)
{
if(opening != null)
opening.style.height = ContentHeight + 'px';

if(closing != null)
{
closing.style.display = 'none';
closing.style.height = '0px';
}
return;
}

timeLeft -= elapsedTicks;
var newClosedHeight = Math.round((timeLeft/TimeToSlide) * ContentHeight);

if(opening != null)
{
if(opening.style.display != 'block')
opening.style.display = 'block';
opening.style.height = (ContentHeight - newClosedHeight) + 'px';
}

if(closing != null)
closing.style.height = newClosedHeight + 'px';

setTimeout("animate(" + curTick + "," + timeLeft + ",'"
+ closingId + "','" + openingId + "')", 33);
}//End of Accordeon

================================

/*reflection.js for jQuery v1.02*/

(function($) {

$.fn.extend({
reflect: function(options) {
options = $.extend({
height: 0.33,
opacity: 0.5
}, options);

return this.unreflect().each(function() {
var img = this;
if (/^img$/i.test(img.tagName)) {
function doReflect() {
var reflection, reflectionHeight = Math.floor(img.height * options.height), wrapper, context, gradient;

if ($.browser.msie) {
reflection = $("<img />").attr("src", img.src).css({
width: img.width,
height: img.height,
marginBottom: -img.height + reflectionHeight,
filter: "flipv progid:DXImageTransform.Microsoft.Alpha(opacity=" + (options.opacity * 100) + ", style=1, finishOpacity=0, startx=0, starty=0, finishx=0, finishy=" + (options.height * 100) + ")"
})[0];
} else {
reflection = $("<canvas />")[0];
if (!reflection.getContext) return;
context = reflection.getContext("2d");
try {
$(reflection).attr({width: img.width, height: reflectionHeight});
context.save();
context.translate(0, img.height-1);
context.scale(1, -1);
context.drawImage(img, 0, 0, img.width, img.height);
context.restore();
context.globalCompositeOperation = "destination-out";

gradient = context.createLinearGradient(0, 0, 0, reflectionHeight);
gradient.addColorStop(0, "rgba(255, 255, 255, " + (1 - options.opacity) + ")");
gradient.addColorStop(1, "rgba(255, 255, 255, 1.0)");
context.fillStyle = gradient;
context.rect(0, 0, img.width, reflectionHeight);
context.fill();
} catch(e) {
return;
}
}
$(reflection).css({display: "block", border: 0});

wrapper = $(/^a$/i.test(img.parentNode.tagName) ? "<span />" : "<div />").insertAfter(img).append([img, reflection])[0];
wrapper.className = img.className;
$.data(img, "reflected", wrapper.style.cssText = img.style.cssText);
$(wrapper).css({width: img.width, height: img.height + reflectionHeight, overflow: "hidden"});
img.style.cssText = "display: block; border: 0px";
img.className = "reflected";
}

if (img.complete) doReflect();
else $(img).load(doReflect);
}
});
},

unreflect: function() {
return this.unbind("load").each(function() {
var img = this, reflected = $.data(this, "reflected"), wrapper;

if (reflected !== undefined) {
wrapper = img.parentNode;
img.className = wrapper.className;
img.style.cssText = reflected;
$.removeData(img, "reflected");
wrapper.parentNode.replaceChild(img, wrapper);
}
});
}
});

})(jQuery);


I would like to think that it is something easy Matt? I'm about to throw the lap top out of the window at the moment!!

Many thanks

--
Ely
13-Apr-11 02:29
Matt, when I add the Add On DIVX Plus web player, and I place my mouse over an image, the error pop up appears with the details 'error: tagName is not an object;

When I disable this Add On I can drag the image but not drop it into the shopping cart. I've been trying for days now and this project has to be completed. Got any ideas?

Many thanks

--
Ely
16-Apr-11 20:05
@Ely: I would disable all add-ons to start with, in case any of them are causing the problem.

When you see the "error on page" warning triangle, double-click it to get the full message (including the line number where the error occurred).

--
Matt Doyle, Elated
05-Jun-11 19:31
can you tell me how to save the numbers who have moved, you can do with php and mysql, please I need a solution
07-Jun-11 07:20
@ggmac23: Best way would probably be to make an Ajax request within handleDropEvent(). You'd send the ID of the card in the request to your PHP script, which would then store the ID in your database table.

You can easily make Ajax requests using jQuery:

http://api.jquery.com/jQuery.ajax/

HTH,
Matt

--
Matt Doyle, Elated
15-Jun-11 12:22
Hello Matt,

Nice tutorial, but I am having one problem. I'm an eLearning developer and plan to use this for a "matching" interaction. My client has a population of 150K employees on IE6, IE8 or FF3, majority (90%+) on IE6. This interaction works fine in IE8 and FF3, and initially in IE6. In IE6 when I try to move a draggable that's already been dropped on one of the five targets, it won't let me. I've commented out everything in the handle Drop Event function except the ui.draggable.position statement in order to write my own validation routine. I tried to explicitly set ui.draggable.draggable to enabled, but still a no go. I'm also not reverting when dropped on an incorrect target. Validation will be handled via a Submit button that will grade the interaction all at once with a second attempt allowed if three or more are incorrect.

Any ideas?

Thanks,
Ray

[Edited by RSugelSr on 15-Jun-11 17:03]

--
Ray
16-Jun-11 14:19
Not sure what's going on with IE6, yesterday it didn't work, today it does.

--
Ray
23-Jun-11 03:30
@RSugelSr: That sounds like the IE6 we all know and love Glad it's working now anyway!

--
Matt Doyle, Elated
12-Jul-11 11:56
Hey Matt,

Really great tutorial. I'm just having an issue. In my handleImageDrop function, the ui.draggable.data() values are not coming through. Can you tell me what I could be doing wrong?

Thanks!


40 for ( var i=0; i<={{ num_products }}; i++ ) {
41 elixirQuery("#img_productImage" + i).draggable( {
42 //containment: '#content',
43 cursor: 'move',
44 revert: true,
45 helper: 'clone',
46 } );
47 }
48 elixirQuery("#div_wishlist").droppable( {
49 //accept: '#div_productList div',
50 hoverClass: 'hovered',
51 drop: handleImageDrop,
52 });
53 function handleImageDrop( event, ui ){
54 var product_id = ui.draggable.data( 'title' );
55 var product_img_src = ui.draggable.data( 'src' ); 56 alert("The product_img_src is: " + product_img_src);
57 alert("The product_id is: " + product_id);
58
59
60 jQuery.ajax({
61 type: "POST",
62 url: '/wishlist/add/' + product_id + '/',
63 data: "",
64 success: function(){
65 jQuery("#table_wishlist").append('<tr><td><img src="' + product_img_src + '" width=80 height=80</td></tr>');
66 },
67 });
68 }


--
Mike
14-Jul-11 04:03
@mittelman: Where and how are you creating the 'title' and 'src' data keys on your draggable elements?

--
Matt Doyle, Elated
14-Jul-11 08:36
Matt - Forgive my noob-ness at javascript! I was expecting those to be the src and title of my <img> tag that is being dragged. Is there a way to get that info? The biggest problem is that I'm using a template to render my html and there could be 2 or 30 draggable objects, so it's hard to give them all a unique id and know how to select them using jquery selectors. Thanks for your hlep!

--
Mike
17-Jul-11 21:43
Matt - I got it...i needed to call the .attr() function instead of .data(). Thanks its working great now!

--
Mike
18-Jul-11 23:43
@mittelman: Ah I see what you were trying to do now. Yes, data() is for associating extra data with an element via jQuery, and has nothing to do with the element's attributes.

Glad you got it working!

--
Matt Doyle, Elated
22-Jul-11 06:20
How would I position the current clicked droppable item to land on top position? For example, while viewing photos with the basic demo the photo keeps its current position in the stack.
22-Jul-11 07:54
@eyveneen - I'm no expert, but I'd say you can do something like this:

var oldHtml = $("div_target").html();
var newHtml = newStuff + oldHtml;
$("div_target").html(newHtml);

So you're basically putting the "newStuff" at the beginning and adding the existing stuff to the end of it.

Make sense? Hope that helps!

--
Mike
20-Sep-11 04:53
Hello and sorry for my noobishness..but maybe you where born as a noob too!
I really like this tutorial!
But my problem is as followed:
I like too have some moveable icons which I like to dorp on a Droppable Element. After dropping the icon each icon can open a different Page.
In your tutorial it opens the alert window...how can I change it to a new window for each icon?
THX
23-Sep-11 02:38
@gromo: No problem, we were all noobs once

You can open a new window on a drop event - something like this:


function handleDropEvent( event, ui ) {
var draggable = ui.draggable;
var iconId = draggable.attr('id');
window.open( 'newPage.php?id='+iconId, 'newWindow' );
}


--
Matt Doyle, Elated
03-Oct-11 14:17
Many thanks for taking the time to put up this tutorial. It's just what I needed to get my started on a drag and drop task I have.

I'm using Grails for my project - do you have any views on the best ways to integrate Grails and jQuery? I know about the jQuery plugin(s) but wondered if I can get away without them for features such as in your tutorial. I just need the Javascript right? I'd like to keep the technology salad as simple as possible.

Thanks again.

Chris

--
Chris Ward
05-Oct-11 00:46
@Chris: I don't know anything about Grails I'm afraid. It sounds like it's a server-side web app framework though? In which case it shouldn't interfere with jQuery at all.

--
Matt Doyle, Elated
06-Oct-11 11:11
Thank you for posting this JQuery Drag and Drop tutorial! It's very well written.

I'm sorry but the cards weren't visible in the card game in ie9. They work fine in Safari 5.1 though.

Thanks again for posting this tutorial.
10-Oct-11 05:56
@markw707: Upgrade to a newer version of jQuery to fix the IE9 issue:

http://bugs.jquery.com/ticket/8052

--
Matt Doyle, Elated
03-Jan-12 10:33
Great tutorial. Thanks so much for this!

I'm somewhat new to jQuery and JavaScript, and I've been struggling through a drag-drop problem with no luck.

Is it possible to prevent one draggable from being dropped upon another draggable?

For example, if I created a tic-tac-toe board where each square is a droppable, and each X and O is a draggable, I would want the draggable to revert if I attempted to drop it on a square that already contained an X or O. I'd also want the droppable to become active again if a draggable is removed from it.

Any advice? Thanks in advance.
03-Jan-12 11:03
Hi brianeoneill,

I do something similar to this in my app. The key thing for me was to maintain a "model" (in your case you could have a 3x3 array representing the drop cells) - i.e. don't just try to get the info from the visual components.

If the model for the cell is "empty" you can allow the drop and update the model for that cell. If it's already filled (with an "O" or an "X") you could maybe use the revert setting to send the draggable back to where it came from.

You'll need to determine the row/col coordinates from the drop position in order to reference your model, but you can do then by inspecting the position attributes of the draggable relative to the droppable (or you may have a droppable for each separate cell in your game grid).

Hope this helps.

Chris

--
Chris Ward
03-Jan-12 11:17
@Chris Ward
Thanks for the reply. I'll explore that avenue. Forgive my noob-ness, but can you (or anyone else) recommend a good resource for learning more about creatingin/maintaining a model?

Best,
B
03-Jan-12 13:13
Hi,

I wanted to get back sooner rather than later but (since time is in short supply) please forgive any glaring errors in "indicative" code below. What I did was take my own Model "class" in JavaScript at attempt to b@stardise it to match your example.

What I am trying to show it holding the state of your game in an abstraction - in this case the "class" Model. Then you can add the handy methods/functions to that to simplify the interactions. For example see the use of


if(model.isUndefinedRowCol(row, col)) {


Which is the main thing you're after I think.

Hope it helps - it's bit rushed but should give some idea of what I meant...





var Model_UNDEFINED = "_"

function Model() {
this.rows
this.rowCount
this.colCount

this.init = function(rowCount, colCount) {
this.rowCount = rowCount
this.colCount = colCount
this.rows = new Array(rowCount);

for ( var i = 0; i < rowCount; i++) {
this.rows[i] = new Array(colCount);
}
this.reset();
}

this.toString = function() {
var retString = "";

for ( var i = 0; i < this.rows.length; i++) {
for ( var j = 0; j < this.rows[i].length; j++) {
retString += this.rows[i][j];
}
retString += "\n";
}
return retString;
}

this.reset = function() {
for ( var i = 0; i < this.rows.length; i++) {
for ( var j = 0; j < this.rows[i].length; j++) {
this.rows[i][j] = Model_UNDEFINED;
}
}
}

this.setRowCol = function(row, col, value) {
if((row == null) || (col == null)) {
return;
}

this.rows[row][col] = value;
}

this.resetRowCol = function(row, col) {
if((row == null) || (col == null)) {
return;
}

this.rows[row][col] = Model_UNDEFINED;
}

this.getRowCol = function(row, col) {
if((row == null) || (col == null)) {
return;
}

return this.rows[row][col];
}

this.isUndefined = function(item) {
if(item == null) {
return false;
}

return item == Model_UNDEFINED;
}

this.isUndefinedRowCol = function(row, col) {
if((row == null) || (col == null)) {
return;
}
var item = this.getRowCol(row, col)
return this.isUndefined(item)
}

}




Use the above in your HTML (script) code with this sort of thing ...




var model;

function init() {
model = new Model();
model.init(3, 3);
...

.droppable({
...
drop : handleDrop,
...
});

}


function handleDrop(event, ui) {

// work out the row and col you've dropped it on

if(model.isUndefinedRowCol(row, col)) {

// allow the drop
...
ui.draggable.draggable( 'option', 'revert', false );


// update the model
model.setRowCol(row, col) = ui.draggable.data('playerSymbol');
} else {
ui.draggable.draggable( 'option', 'revert', true );
}


}




--
Chris Ward
03-Jan-12 13:41
hi Matt, I liked your tutorial on drag and drop features in jQuery.

I am now looking for a drag and drop feature either between two windows (same browsers) or two frames in a window. I saw the jQuery UI documentation and did not find any useful object or method to work on this feature.

Can you please help me or direct me towards some documentation or any article showcasing this feature.
03-Jan-12 15:01
@Chris Ward,

Wow, thank you SO much for taking the time for such a detailed response. I'm going to give that a try. I'll be sure to share the results.

Best,
Brian
11-Jan-12 16:51
@Chris Ward: Great answer

@mclipwala: Not sure if this would be possible using jQuery UI. You might be able to get it to work using HTML5 drag-and-drop (obviously only in newer browsers that support it):

http://stackoverflow.com/questions/3694631/html5-drag-and-drop-between-windows

http://www.html5rocks.com/en/tutorials/dnd/basics/

--
Matt Doyle, Elated
12-Jan-12 17:49
Matt,
Great demo... really breaks it down quite well for a true beginner such as myself.

I've modified the demo a bit so that the destination pile is vertically stacked, not horizontal. Then I increased the width of the destination pile so it is about 80% the width of the page.

I've left the source pile alone so it is the same size. When I drag the card onto the proper destination, I want the card to snap to the same width as the destination div. I tried setting the CSS as follows:

#card3.correct { background: orange; width:500px;}

But doing so in this manner causes problems for the source pile.

Is there a way to make the small draggable card snap to the full width of the destination div?

Any help is appreciated.

Thanks,

--Rick
16-Jan-12 03:25
@rpdwyer: Please post the URL of your page so we can see the problem.

--
Matt Doyle, Elated
16-Jan-12 07:02
@rpdwyer

I think I've encountered the same sort of thing. I put it down to the fact that the <div> item actually exists in the source pile so when you change the width via the style then it will "stretch" the containing source pile.

As I recall I found that there were cases were I ended up creating a *new* <div> item (a sort of clone) and then placed that in the destination and could style it without impacting the source.

I am quite new to jQuery and I've played around with lots of things in drag and drop so I can't be very specific.

--
Chris Ward
02-Feb-12 13:03
Hi all,

Want to combine this example with a gallery-like feature where cardpile and cardslots have prev/next links at left and right such that a user can scroll horizontally through a number greater than 10. How best to do it?

Thanks..
Robin
02-Feb-12 23:16
@rgsamways: Off the top of my head...

Try adding the prev/next links, then add click handlers to the links that increase/decrease the margin-left CSS property of the cardPile/cardSlots divs appropriately. Use CSS transitions if you'd like a smooth slide between each position.

Also wrap each div in an outer div with the same dimensions, and apply overflow: hidden to the outer divs. That way the cardPile and cardSlots divs will scroll horizontally within the "window" of the outer div.

--
Matt Doyle, Elated
03-Apr-12 08:21
Really nice demo. Works fine in Firefox and Chrome, but the drag and drop didn't work in IE8.0.

I try on my Android Tablet to test the demo and I found out that the drag and drop doesn't work well. When I try to drag a card, it's the all page that is dragged instead.

Do you have a version for tablet or a ideas of what I should adapt to make it work ?
03-Apr-12 09:13
@survivant

I've felt that pain.

This took it away for me within minutes of discovering it :

http://furf.com/exp/touch-punch/draggable.html

Hope it works for you too.

Regard,
Chris

--
Chris Ward
10-Apr-12 14:40
I would like to ask if there is a way to use the objects as a part of form submission. For example, the user will only know the correct placement of the draggable object after clicking the SUBMIT button instead of real time results.
10-Apr-12 15:38
k_ace,

I used Elated's D & D as the basis for an eLearning activity. Not a form submission but the same concept. Had to write additional methods, but the basic premise is to compare a drag's final position to the correct position. After clicking the "Submit" button, it validates visually indicating which items were placed correctly/incorrectly and then displays a "Solution" button which moves all drags to their correct positions.

--
Ray
13-Apr-12 09:06
Hi,

Firstly I love the plugin, it works great for what I want it to do!

I have used the card-draggable code as I have created a system to approve designs based on the user dragging Object A to Object B then firing a SQL statement.

My problem is when adding the divs, namely:



<div id="cardPile"> </div>
<div id="cardSlots"> </div>
<div id="successMessage"></div>


I can only see 1 of the drag container, but my project pulls the main container - *with the 'cardpile' inside* based on the amount of entries in the database. i.e there are 8 designs, it pulls 8 containers, but this plugin only pulls into the first one. I assume it's because of the ID?

Can you please help with how to have multiple entities of these divs on one page?

Appreciate any assistance!
18-Apr-12 13:47
@ChrisWard, your information about the Model 'class' for handling a grid-based game layout is something I've been trying to figure out myself for the past week or so... I just didn't realize what exactly I was looking for!

That was a great example, but I was wondering if you know of any other resources that go into the concept a bit deeper; I haven't come across any tutorials or training that really lays it out.

@matt, this article has been invaluable as well!
19-Apr-12 06:18
@minttoothpick - thank you very much, I am pleased if it helped in any way.

I don't really know of any other resources. I didn't find anything on the web to get me going down this route.

There were three influences that drove me to use a "model" -

a) I am using Grails and it's clear distinction between Model, View and Controller were at the front of my mind at the time. I've done a lot of GUI application work over the years to tend to use this approach as default

b) Complexity of what I wanted to store - my application needs to maintain pretty darn complex state and I needed to have it in an actual data structure rather than rely on the DOM states.

c) Load/Save - My application requires me to be able to persist my visual representation and be able to reload it. Once you have your stuff represented in a data structure (the "model" in JavaScript with the member fields and the getters and setters and convenience functions) it's not too difficult to puke that out in something like JSON. Once you've done it in that direction (saving) you can do the other direction (loading). I tend to have a function called


inflate(jsonString)


in my models and it just takes the JSON string input and calls a library function to reestablish the proper model instance from it.

I don't know if this helps at all. My general advice would be to think about a model/data structure that would hold all the state(s) your thing needs. Maybe even write some tests to use the model until you're happy with it. At that point you can move to doing the visual stuff on top.

One of the first things the appealed to me with jQuery was the ability to attach data to visual objects using


$(locator).data(your-bit-of-data)


Once you do that it makes it nice and clean to access this data inside a drop method (if you're using drag and drop) and use that as the param for your calls to your model functions.

Not sure if this makes it clearer or most confusing!

Chris

--
Chris Ward
19-Apr-12 07:40
@ChrisWard - Thanks again for your comments. I have only been working with JavaScript for several months, and it was in the past two weeks that I started using jQuery. Discovering the .data() method has been very helpful.

Currently I am putting together a checkers board game. I started by implementing the <canvas> element and tried a couple related JS libraries for working with it, but it proved too complicated. Now I'm working with jQuery and <div> elements to create the game board, but I am still figuring out how to best reference the individual cells for the game logic.

I haven't delved into JSON yet but I've been hearing about it a lot.

Anyways, I appreciate that you have taken the time to provide such valuable information!
19-Apr-12 15:53
Hi, your guide is wonderful and very helpful.

However, I am having a problem. I am using a function to create a helper (instead of clone), but I am trying to pass a variable to this helper. More specifically, I am trying to pass the inner html of the original draggable element to the helper element.

Here is my very lame attempt:



function catHelper(event, ui){
var name=ui.draggable.html();
return '<div class="cat_helper">'+name+'</div>';
}


--
nico
20-Apr-12 04:10
@minttoothpick

One way to do what you need is to have <div>s for the black and white pieces and 8x8 <div>s for the playing board.

If you want to identify the colour of a piece (or counter or whatever you want to call it) you could apply a .data("black") or .data("white"). However - you might want to use the css class attribute since you'll probably be needed to style these <div>s to have a black or white image.

Then on the board each of the squares could have data in the range .data("1,1") ... .data("8,8").

If you make the board squares droppable, and the playing piece draggable then when you drop a piece onto a square you should be able to work out the piece colour and the grid reference in the handleDrop function (which gives you a reference to both the draggable and the droppable)

Once you have these you could call your model method like

model.setCell( x, y, pieceColour )

Something along those lines should work I'd have thought.

--
Chris Ward
27-Apr-12 19:46
@minttoothpick: If you want to learn JSON then you might enjoy my tutorial:

http://www.elated.com/articles/json-basics/

@nicolasmoise: Off the top of my head, you probably want to create a closure, passing in the draggable element so that your function can access it.

--
Matt Doyle, Elated
18-May-12 02:11
thanks for the great dnd tutorial, Matt!
In the tutorial, the numbers get located randomly in the Card Pile, and the Card Slots are located 1-10, left to right --- Card "1" will always drop on the first Slot.
I want to build a game where you don't know which 'Correct' slot the number should drop into - in other words, so slot "1" (array position 0) locates randomly in the row of 10 Card Slot locations, instead of always in the first (left-most) location, but i've had no luck 'sorting' the Slots to any other position.

any ideas?

tia
02-Jun-12 17:05
First of all, this is a great introduction to jQuery UI and drag and drop! I found it immensely helpful when getting up to speed and implementing it in to my project.

One issue I came across was reassigning the parent div of the draggable object when a drop occurred. You can get some positioning weirdness.

If anyone runs across the same problem, you can see my solution here: http://eightbitswitch.com/2012/06/jquery-ui-drag-and-drop-to-change-parent-div/
15-Jun-12 02:20
@xkater: Can you not just call the sort() method on the words array, in the same way as the numbers array?

@danahartweg: Thanks for posting that solution

--
Matt Doyle, Elated
03-Jul-12 10:47
Excellent article, very helpful.

I have a project in mind, so will try to get to grips with this

Taking the card example as a starting point, I would like to add multiple instances of the same card(s) and stack them on top of each other, (or nearly on top, so you can tell its a stack).

Grateful for an approach
14-Aug-12 21:26
Great article.

I would like to set this up for my students where they can practice vocabulary, but I am still relatively new to jquery.

How would I go about replacing the numerals with images so that an image could be dragged onto the correct word?

Thanks in advance!
09-Oct-12 22:40
Superb article, everything is crystal clear, thanks a lot for finding time to compose this

cheers

sameera
18-Oct-12 04:06
That's cool

--
Hello
09-Nov-12 13:00
Hi, Matt. I've found your code great, but I've run into a problem with Chrome: when the user zooms in or out ,the cards refuse to drop in their intended positions. Have you developed a solution yet? Thanks a lot for the code and for your answer!
21-Nov-12 21:29
@piapoco: Looks like a general problem with any jQuery UI draggable. Try zooming in then dragging "I only snap to the big box" - it no longer snaps to the big box edges: http://jqueryui.com/draggable/#snap-to

--
Matt Doyle, Elated
26-Nov-12 13:48
Thanks so much for the guide. This was my first try at using drag and drop (and pretty much jQuery in general) and you walked me through it easily. Great article and helpful links to the documentation.
17-Dec-12 15:52
@goldilaks You're welcome - thanks for your feedback.

--
Matt Doyle, Elated
20-Dec-12 01:18
Many thanks for this great tutorial!! I would like to create the slot divs arranged in a circle, perhaps using the following:



<script type="text/javascript">
$(document).ready(function(){
var radius = 200; // radius of the circle
var fields = $('.field'),
container = $('#container'),
width = container.width(),
height = container.height(),
angle = 0,
step = (2*Math.PI) / fields.length;
fields.each(function() {
var x = Math.round(width/2 + radius * Math.cos(angle) - $(this).width()/2),
y = Math.round(height/2 + radius * Math.sin(angle) - $(this).height()/2);
$(this).css({
left: x + 'px',
top: y + 'px'
});
angle += step;
});
});
</script>

</head>

<body>
<div id="container">
<!-- <div id="center"></div>
<div id="crosshair-x"></div>
<div id="crosshair-y"></div>-->
<div class="field">1</div>
<div class="field">2</div>
<div class="field">3</div>
<div class="field">4</div>
<div class="field">5</div>
<div class="field">6</div>
<div class="field">7</div>
<div class="field">8</div>
<div class="field">9</div>
</div>


Any ideas on how I can incorporate this into the drag and drop code so that it works with 9 slots and cards arranged in a circle?

[Edited by peterbrowne on 20-Dec-12 02:11]
01-Jan-13 04:51
some of users of my website cannot see draggable box in IE9.
what can i do ?
01-Jan-13 16:45
Use conditional comments to show an alternative.

--
Chris.
So long, and thanks for all the fish.
http://webmaster-talk.eu/
18-Feb-13 15:30
Apologies for the possibly n00b question, but how to change the first numerical array in the card game example to a string array so it still matches the cards correctly. ie change this array:


var numbers = [ 1, 2,


to a string array of words (for example):


var numbers = [ 'un', 'deux'


I presume the randomizing '.sort' function is preventing correct matching if the numerical array is changed to a string array? Anyone done a non-numerical matching game?

But anyway thank you @Matt for the excellent tutorial, I like learning from real-world examples like this and you've explained it all so clearly!

[Edited by DIFowler on 18-Feb-13 20:12]
18-Feb-13 20:30
can the numbers[] variable filled with image instead of number?


var numbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 ,20 ];
28-Feb-13 22:18
I am using ubuntu and firefox. The demo works online but if you copy the html and style code from this web page and run on your computer it doesn't. You can not drag the cards past slot number 8. You can not drag cards to slot nine or ten. I added a width of 100% to the #containment div. It worked o.k. Why would it work online and not on the local computer?

[Edited by cantrecall on 01-Mar-13 08:59]
29-Mar-13 15:56
I am very happy about your information!! It is genial and helpful!!
But it does not run on my android phone. I cannot drag anything. The cards do not move.
I am a teacher in Austria and I want to develop questions with drag & drop for my students.
Most of them using their smartphones.
What should I do to make it run on smartphones and tablets???
Thank you very much and greetings from Salzburg!

Uwe
31-Mar-13 11:57
Hi Uwe,

I use drag and drop a lot based originally on this article. I also use TouchPunch to extend to drag and drop for iPad but I recently noticed that the only tablet that failed to work was one using the Android browser.

I appreciate is is not much help to you but I would also be interested in the answer.

Chris Ward

--
Chris Ward
31-Mar-13 15:46
Hi Chris,

thank you very much for your mail! I found a solution with your help and it worked on my android phone also.
I made some modifications of your example: www.ats.eu.com/drop and I will combine it with my database to develop more examples....

Best wishes
Uwe
30-May-13 10:52
Hi Matt,
I'm not sure if you are still following this post or not, but any how, I wanted to check with you about some tweak that I made to your code.

My goal is to use this drag and drop for sorting type of exercises for our users.

And I will include the code that i have changed here. My question is how to get the order in which the user has placed his choices in cardSlots. That is, I want to save the user's answer.

Is there any way to do this?

Thanks in advance.


<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Test.aspx.vb" Inherits="Project.Test"
EnableEventValidation="false" ValidateRequest="false" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit.HTMLEditor"
TagPrefix="cc1" %>
<!DOCTYPE html" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head runat="server">
<title>Sorting Exercise</title>
<style type="text/css">
/* Add some margin to the page and set a default font and colour */

body
{
margin: 30px;
font-family: "Georgia" , serif;
line-height: 1.8em;
color: #333;
}

/* Give headings their own font */

h1, h2, h3, h4
{
font-family: "Lucida Sans Unicode" , "Lucida Grande" , sans-serif;
}

/* Main content area */

#content
{
margin: 80px 70px;
text-align: center;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}

/* Header/footer boxes */

.wideBox
{
clear: both;
text-align: center;
margin: 70px;
padding: 10px;
background: #ebedf2;
border: 1px solid #333;
}

.wideBox h1
{
font-weight: bold;
margin: 20px;
color: #666;
font-size: 1.5em;
}

/* Slots for final card positions */

#cardSlots
{
margin: 50px auto 0 auto;
background: #ddf;
}

/* The initial pile of unsorted cards */

#cardPile
{
margin: 0 auto;
background: #ffd;
}

#cardSlots, #cardPile
{
width: 200px;
height: 400px;
padding: 0px;
border: 2px solid #333;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: 0 0 .3em rgba(0, 0, 0, .8);
-webkit-box-shadow: 0 0 .3em rgba(0, 0, 0, .8);
box-shadow: 0 0 .3em rgba(0, 0, 0, .8);
}

/* Individual cards and slots */

#cardSlots div, #cardPile div
{
float: left;
width: 80%;
height: 20px;
padding: 5px;
padding-top: 5px;
padding-bottom: 5px;
border: 2px solid #333;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
margin: 0 0 0 10px;
background: #fff;
}

#cardSlots div:first-child, #cardPile div:first-child
{
/*margin-left: 0;*/
}

#cardSlots div.hovered
{
background: #aaa;
}

#cardSlots div
{
border-style: dashed;
}

#cardPile div
{
background: #666;
color: #fff;
font-size: 12px;
text-shadow: 0 0 3px #000;
}

#cardPile div.ui-draggable-dragging
{
-moz-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);
-webkit-box-shadow: 0 0 .5em rgba(0, 0, 0, .8);
box-shadow: 0 0 .5em rgba(0, 0, 0, .8);
}

/* Individually coloured cards */
#card0.correct
{
background: silver;
}
#card1.correct
{
background: red;
}
#card2.correct
{
background: brown;
}
#card3.correct
{
background: orange;
}
#card4.correct
{
background: yellow;
}
#card5.correct
{
background: green;
}
#card6.correct
{
background: cyan;
}
#card7.correct
{
background: blue;
}
#card8.correct
{
background: indigo;
}
#card9.correct
{
background: purple;
}
#card10.correct
{
background: violet;
}
#card11.correct
{
background: lime;
}
#card12.correct
{
background: Teal;
}
#card13.correct
{
background: Olive;
}
#card14.correct
{
background: White;
}
#card15.correct
{
background: Navy;
}
#card16.correct
{
background: gray;
}

/* "You did it!" message */
#successMessage
{
position: absolute;
left: 580px;
top: 250px;
width: 0;
height: 0;
z-index: 100;
background: #dfd;
border: 2px solid #333;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
-moz-box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);
-webkit-box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);
box-shadow: .3em .3em .5em rgba(0, 0, 0, .8);
padding: 20px;
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.9/jquery-ui.min.js"></script>
<script type="text/javascript">
var correctCards = 0;
$(init);
//When|we|got|to|the|concert,|the |orchestra| had|already| begun|to|play.
var numbers = [[0, 'When'], [1, 'we'], [2, 'got'], [3, 'to'], [4, 'the'], [5, 'concert'], [6, 'the'], [7, 'orchestra'], [8, 'had'], [9, 'already'], [10, 'begun'], [11, 'to'], [12, 'play']];
var numbersunsorted = [[0, 'When'], [1, 'we'], [2, 'got'], [3, 'to'], [4, 'the'], [5, 'concert'], [6, 'the'], [7, 'orchestra'], [8, 'had'], [9, 'already'], [10, 'begun'], [11, 'to'], [12, 'play']];
function init() {

// Hide the success message
$('#successMessage').hide();
$('#successMessage').css({
left: '580px',
top: '250px',
width: 0,
height: 0
});

// Reset the game
correctCards = 0;
$('#cardPile').html('');
$('#cardSlots').html('');
$('#cardPile').height(35 * numbers.length);
$('#cardSlots').height(35 * numbers.length);


// Create the pile of shuffled cards
numbers.sort(function () { return Math.random() - .5 });
for (var i = 0; i < numbers.length; i++) {
$('<div>' + numbers[i][1] + '</div>').data('number', numbers[i][1]).attr('id', 'card' + numbers[i][0]).appendTo('#cardPile').draggable({
containment: '#content',
stack: '#cardPile div',
cursor: 'move',
revert: true
});
}

// Create the card slots
var words = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];
for (var i = 1; i <= numbers.length; i++) {
$('<div>' + numbersunsorted[i - 1][1] + '</div>').data('number', numbersunsorted[i - 1][1]).appendTo('#cardSlots').droppable({
accept: '#cardPile div',
hoverClass: 'hovered',
drop: handleCardDrop
});
}

}

function handleCardDrop(event, ui) {
var slotNumber = $(this).data('number');
var cardNumber = ui.draggable.data('number');

// If the card was dropped to the correct slot,
// change the card colour, position it directly
// on top of the slot, and prevent it being dragged
// again
if (slotNumber == cardNumber) {
ui.draggable.addClass('correct');
ui.draggable.draggable('disable');
$(this).droppable('disable');
ui.draggable.position({ of: $(this), my: 'left top', at: 'left top' });
ui.draggable.draggable('option', 'revert', false);
correctCards++;
}


if (slotNumber != cardNumber) {
ui.draggable.addClass('incorrect');
//ui.draggable.draggable('enable');
$(this).droppable('disable');
ui.draggable.position({ of: $(this), my: 'left top', at: 'left top' });
ui.draggable.draggable('option', 'revert', false);

}
// If all the cards have been placed correctly then display a message
// and reset the cards for another go

if (correctCards == numbers.length) {
$('<div>' + 'Your Score is: ' + correctCards + ' out of ' + numbers.length + '</div>').appendTo('#successMessage')
$('#successMessage').show();
$('#successMessage').animate({
left: '380px',
top: '200px',
width: '400px',
height: '100px',
opacity: 1
});
}
}

function showResult() {

$('<div>' + 'Your Score is: ' + correctCards + ' out of ' + numbers.length + '</div>').appendTo('#Div1');
$('#Div1').show();
$('#Div1').animate({
left: '380px',
top: '200px',
width: '400px',
height: '100px',
opacity: 1
});
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div id="content" style="width: 40%">
<div id="cardPile" style="float: left;">
<div id="card4" class="ui-draggable" style="position: relative;">
4</div>
<div id="card1" class="ui-draggable" style="position: relative;">
1</div>
<div id="card2" class="ui-draggable" style="position: relative;">
2</div>
<div id="card5" class="ui-draggable" style="position: relative;">
5</div>
<div id="card3" class="ui-draggable" style="position: relative;">
3</div>
<div id="card6" class="ui-draggable" style="position: relative;">
6</div>
<div id="card7" class="ui-draggable" style="position: relative;">
7</div>
<div id="card8" class="ui-draggable" style="position: relative;">
8</div>
<div id="card9" class="ui-draggable" style="position: relative;">
9</div>
<div id="card10" class="ui-draggable" style="position: relative;">
10</div>
<div id="card11" class="ui-draggable" style="position: relative;">
11</div>
<div id="card12" class="ui-draggable" style="position: relative;">
12</div>
<div id="card13" class="ui-draggable" style="position: relative;">
13</div>
<div id="card14" class="ui-draggable" style="position: relative;">
14</div>
</div>
<div id="cardSlots">
<div class="ui-droppable">
one</div>
<div class="ui-droppable">
two</div>
<div class="ui-droppable">
three</div>
<div class="ui-droppable">
four</div>
<div class="ui-droppable">
five</div>
<div class="ui-droppable">
six</div>
<div class="ui-droppable">
seven</div>
<div class="ui-droppable">
eight</div>
<div class="ui-droppable">
nine</div>
<div class="ui-droppable">
ten</div>
<div class="ui-droppable">
Eleven</div>
<div class="ui-droppable">
Twelve</div>
<div class="ui-droppable">
Thirteen</div>
<div class="ui-droppable">
Fourteen</div>
</div>
<div id="successMessage" style="display: none; left: 580px; top: 250px; width: 0px;
height: 0px;">
<button onclick="init()">
Play Again</button>
</div>
<button onmouseover="showResult()" onmouseout="$('#Div1').hide(); $('#Div1').html('');">
Show Result</button>
<div id="Div1" style="display: none; left: 580px; top: 250px; width: 0px;
height: 0px;">
</div>

</div>
</form>
</body>
</html>



[Edited by hitech on 30-May-13 10:53]
03-Jun-13 07:55
Never mind. After reading a bit about JQuery ui, I came to know that we can store multiple values as data with each cell, and it can be the cell location.


$('<div>' + numbers[i]['value'] + '</div>').data('number', { x: numbers[i]['value'], y: numbers[i]['index'] }).attr('id', 'card' + numbers[i]['index']).appendTo('#cardPile').draggable({
containment: '#content',
stack: '#cardPile div',
cursor: 'move',
revert: true
});





$('<div>' + userAnswer[i - 1]['value'] + '</div>').data('number', { x: numbersunsorted[i - 1]['value'], y: numbersunsorted[i - 1]['index'] }).appendTo('#cardSlots').droppable({
accept: '#cardPile div',
hoverClass: 'hovered',
drop: handleCardDrop


Then we can read it after dropping the card into the slot

var slotNumber = $(this).data('number').x;
var slotindex = $(this).data('number').y;
var cardNumber = ui.draggable.data('number').x;
var cardindex = ui.draggable.data('number').y;


Thanks

[Edited by hitech on 03-Jun-13 07:56]
11-Jun-13 01:25
Hi,

Your Tutorial is great but i really need help to create a game using this tutorial.

I need help to show out some random cards after the user has pulled and dropped to the correct slots.

Can anybody help me?

[Edited by noobie on 11-Jun-13 01:28]
11-Jun-13 02:19
@matt
I need help to show out some random card numbers after the user has pulled and dropped the first card number to the correct slots.
25-Jun-13 02:27
Hi experts,
I am a teacher in Austria and I developed some quizzes with 200 questions in a database for my students taking the great ideas of this example.
But some students using a surface RT can not use my quizzes because drag & drop does not work there and it is not possible to install an other browser like Mozilla or Chrome on a RT system!!!!

Do anybody know a solution of this problem?

Thanks from Salzburg
Uwe
25-Jun-13 05:33
It's not often I comment these days, but the specificity and the comprehensiveness of this article qualifies such a response — you've given me precisely what I need.
06-Jan-14 01:46
Nice tutorial. Im trying to modify this drag and drop so that it is possible to drag and drop each number to multiple slots. Is this possible? Can anyone give me a hint?
06-Jan-14 01:46
Nice tutorial. Im trying to modify this drag and drop so that it is possible to drag and drop each number to multiple slots. Is this possible? Can anyone give me a hint?
06-Jan-14 03:56
Hi robinh,

I've not posted on this thread for a while so forgive me if I am repeating anything said by others.

Anyway ... I've done something similar to your requirements in a music notation application I wrote. In that I need to be able to drag from a palette of musical symbols onto the lines of a music stave. Most of the symbols can be placed multiple times, which seems to be what you want to do (kind of).

My first solution was to create a stack of each symbol and thus have many instances of each to drag.

However, I quickly realised that all I needed to do was REPLACE the symbol in the palette once I'd used (dragged) the previous instance.

There's a setting to say "clone" the dragged object.

A quick search for an example yielded ...

$( ".selector" ).draggable( "option", "helper", 'clone' );

See this for details :

http://stackoverflow.com/questions/7878980/jquery-ui-drag-shape-but-keep-a-copy-of-the-original-shape


Hope this helps.

Chris

--
Chris Ward
06-Jan-14 06:23
Hi Chris,

Thank you for your answer.
I have a modified version of this drag and drop where i also save data to mysql.


Tried to add helper "clone" but after it is dragged to a slot it is gone. Im not sure why this happens but maybe because I declare the div's in the html file and not by js.

Will need to do some more testing on this.

Best regards
Robin
06-Jan-14 07:38
Hi again,

Im actually using this example:

http://stackoverflow.com/questions/19826075/jquery-drag-drop-form-hidden-value-inserting-into-php-mysql

Have not got it to work as i want yet.
Does anyone have a tips?

Code is here:

http://jsfiddle.net/qLhke/29/
13-Jan-14 09:38
Hello, first I want to congratulate you for your tutorial which was undoubtedly very well done! Secondly, I would like to do with my students a similar to this application, but it was recorded all in mysql example a student does something in the application and this is ready, while another home can continue. Hugs and success for all! I used the translator for not writing in English, I apologize if something went wrong. Jean Brazil.
16-Jan-14 11:09
Hi, i need my droppable fires multiple drop events, how can i do that? until now it just fires one. Please im desperate!!!

--
alpio
04-Mar-14 23:59
How would you drag a card that's been locked in place back out again
and
either drag it to another drop target (draggable)
or
revert back to its previous position if unsuccessful (revert)
14-Mar-14 05:51
The tutorial is so nice and easy to understand step by step.

In number cards game, its uses numbers and functionality is place to correct place.

But i need the code for letters (a,b,c,d...) given some letters,
we need to frame a correct name.


for example

n t h l i s e

Using the above letters, i drag or move (left and right only) one by one letter and to form the correct name as following.

"senthil" is the correct name.

Please give a reply.

Any help will be appreciated.
15-Mar-14 20:24
Letters are also numbers ... ASCII code numbers.


Array elements can hold letters as well and are referenced by a number, so just as long as the letters are in the array in their correct final order it will work just the same.

[Edited by chrishirst on 15-Mar-14 20:24]

--
Chris.
So long, and thanks for all the fish.
http://webmaster-talk.eu/
31-Mar-14 08:43
Thanks for this awesome tutorial. I'm not a programmer but found it really helpful.

I've got what I hope is a simple question. It's about the array for numbers.

"Then we loop through each element in the numbers array, creating a div card element for each number. Inside the div we place the number, so that it appears on the card in the page. Once we've created the div object, we store the card's number in a 'number' key inside the object by using jQuery's data() method."

Instead of the card number appearing on the draggable card, I would like some custom text. Is there a simple way to do that?
01-Apr-14 01:21
"Instead of the card number appearing on the draggable card, I would like some custom text. Is there a simple way to do that?"

I've been bashing my head against this all morning. If someone could tell me the answer, I'd be so grateful.

While I've been failing to make this happen, I've noticed that if you repeatedly refresh the game, about 10% of the time it will not randomise the cards. This is both on my local development version and also on the live preview (http://www.elated.com/res/File/articles/development/javascript/jquery/drag-and-drop-with-jquery-your-essential-guide/card-game.html). Just keep hitting refresh and you'll see that fairly often it gives you all the dragable cards in non-randomised order (1-10).
01-Apr-14 01:33
If it helps, this is where I've got to so far.

If I change the numbers variable to be like this (there are only seven cards in my game):


var numbers = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ];


then everything displays correctly, however, the cards won't match. You cannot drop them in place.

If I make a new variable to store the text values, like this:

var test = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g' ];


and change the code that creates the cards from this:


$('<div>' + numbers[i] + '</div>')


to be this:


$('<div>' + test[i] + '</div>')


then the cards do not sort and they are also not droppable.
01-Apr-14 21:50
OK, I've finally found the answer. When you create the numbers array, you need to give it multiple properties. Like this:


var numbers = [];
numbers [ 0 ] = {x:1, y:'red'};
numbers [ 1 ] = {x:2, y:'orange'};
numbers [ 2 ] = {x:3, y:'yellow'};
numbers [ 3 ] = {x:4, y:'green'};
numbers [ 4 ] = {x:5, y:'sky blue'};
numbers [ 5 ] = {x:6, y:'indigo'};
numbers [ 6 ] = {x:7, y:'violet'};


All you need to do then is define which property you want when you're making the card stack:


for ( var i=0; i<7; i++ ) {
$('<div>' + numbers[i].y + '</div>').data( 'number', numbers[i].x ).attr( 'id', 'card1' ).appendTo( '#cardPile' ).draggable( {
containment: '#content',
stack: '#cardPile div',
cursor: 'move',
revert: true
} );
}
08-Apr-14 04:55
It looks like no-one is following this, but all the same, the following css will fix the cursor (which changes to 'text select' when hovering over cards/slots):



#cardSlots {
cursor: default;
}

#cardPile {
cursor: move;
}
08-Apr-14 05:05
An updated, improved version of the above:



/* Cursors */
#cardPile div {
cursor: move;
}

#cardSlots {
cursor: default;
}

.correct {
cursor: default;
}
20-May-14 05:33
Hello,

http://davidmaillard.fr/projets/dragdrop/

I'm trying to run a test. The idea is to allow the card (draggable) to be able to position it on any cartridge (droppable).

But my problem is at the 'revert' option. Indeed, as the first movement is not enabled, the option 'revert' is enabled. Then after moving, the option is disabled.

Thereafter, if I want to move the card again and I do not put me on a cartridge, the cart does not return to its original location because the option 'revert' was previously off.



function handleCardDrop( event, ui ) {
ui.draggable.position( { of: $(this), my: 'left top', at: 'left top' } );
ui.draggable.draggable( 'option', 'revert', false );
}



Have you an idea to reactivate the option 'revert' on the cart in order to allow more travel?

Thank you in advance
20-May-14 05:59
The solution was finally change this line:



ui.draggable.draggable( 'option', 'revert', 'invalid' );
12-Jul-14 08:44
hello,

first of all, i'am a newbie.

i am looking for this kind of tutorial but find this things only in flash.

this tutorial was almost the perfect example for me because i want to make a sort of quiz for my soccer team (the kids are 10 years old)

my idea was to drag de numbers to te correct places on te soccerfield

the "cardslots" must be placed on the page (soccerfield) in a 433 system

this is where its going wrong i cant place them in other places

can somebody help me or give me the right directions

thanks

ps sorry for my pour english

harold
09-Oct-14 19:52
Hi, I have a situation where the object to be dragged and dropped is beneath a covering image. I don't know how to move the focus of the drag from the top image to the draggable object below it, any ideas?

Any help much appreciated, thanks

Mike

PS: thanks for the plain english explanation of the jQuery, very helpful. I am using Edge Animate to try and get the interactivity working.
25-Oct-14 18:10
This is such a fantastic tutorial thank you so much.

I am trying to create something very similar but instead of numbers/words I want to drag and drop an image.

I have played around with the code but have not have any luck, is anyone able to assist me.

Thanks

 
New posts
Old posts

Follow Elated