Drag. Drop. Lock.
I've received dozens of emails about my Six Degrees of Kevin Bacon Using MooTools article. The MooTools in my article contained a lot of conditional code to require correct dropping per the game and many people requested that I simplify the process and just explain the drag, drop, lock process. Ask and you shall receive!
Step 1: The XHTML
<!-- holders --> <div id="droppable-holder"> <div id="holder1" class="droppable"></div> <div id="holder2" class="droppable"></div> <div id="holder3" class="droppable"></div> <div id="holder4" class="droppable"></div> <div id="holder5" class="droppable"></div> <div id="holder6" class="droppable"></div> </div> <div class="clear"></div> <!-- images --> <div id="dragable-holder"> <div class="dragable" id="dragable1">Drag Me 1</div> <div class="dragable" id="dragable2">Drag Me 2</div> <div class="dragable" id="dragable3">Drag Me 3</div> <div class="dragable" id="dragable4">Drag Me 4</div> <div class="dragable" id="dragable5">Drag Me 5</div> <div class="dragable" id="dragable6">Drag Me 6</div> </div><div class="clear"></div>
The XHTML is very basic. You create your dragables, you create your droppables. I gave each an id attribute, but that isn't necessary for the sake of this example.
Step 2: The CSS
.clear { clear:both; } #dragable-holder { margin:20px 0; } .dragable { position:relative; cursor:move; width:100px; height:70px; border:1px dotted #ccc; float:left; margin:0 20px 0 0; padding:30px 0 0 0; text-align:center; } #droppable-holder { } .droppable { border:2px solid #ccc; width:100px; height:100px; float:left; margin:0 20px 0 0; } .mo { background:#eee; } .locked { cursor:default; background:#fffea1; } .locked-border { border:1px solid #fc0; }
None of the CSS is required, per say, but you always want to make things look good.
Step 3: The MooTools JavaScript
//when the dom is ready... window.addEvent('domready',function() { document.ondragstart = function () { return false; }; //IE drag hack //for every dragable image... $$('.dragable').each(function(drag) { //make it dragable, and set the destination divs new Drag.Move(drag, { droppables: '.droppable', onDrop: function(el,droppable) { if(droppable.get('rel') != 'filled') { //inject into current parent el.inject(droppable).addClass('locked'); el.setStyles({'left':0,'top':0,'position':'relative','margin':0}); //hack -- wtf? droppable.set('rel','filled'); this.detach(); } }, onEnter: function(el,droppable) { //colors! droppable.addClass('mo'); }, onLeave: function(el,droppable) { droppable.removeClass('mo'); } }); drag.setStyles({ 'top':0, 'left':0, 'margin-right':'20px' }); }); });
The dragging and dropping is very self-explanatory. The part to note is the drop event. This is where we inject the dragable into the droppable and detach the element from the list of dragable elements.
Once I removed all of the "Six Degrees" specific code, the Moo to drag, drop, and lock is super easy!
Pretty cool stuff! Thanks!
Great example – the one critique is that if you drop multiple boxes on the same container it messes up, but this is an example item, so understandably people should code for this too when implementing.
@Paul: Awesome catch! I didn’t notice that. Hopefully I’ve have some time to hit that this weekend.
@Paul: I’ve updated the post to disallow dropping more than one DIV in a droppable.
Is it anyway possible to lock them entirely so that if you call refresh the
page the boxes will stay where you droped them ?
@Tim: Definitely. I would recommend using cookie functionality to do this. You can see a great example here:
http://davidwalsh.name/save-text-size-preference-mootools-12
@David: But with a cookie it will only be each visitor who will be able to see the change they do … It need to be only one person who make the changes and then everybody else who enter the site should see the change. So I was thing abit about mysgl php something .. ? Have you seen it in use somewhere maybe ?
@Tim: I get you now. Yep, you’ll make an Ajax call that records the dragable element and which droppable it fell into. I don’t have a specific example now, but maybe in the future.
Hi David
Excellent tutorial, its proved to be very useful so far. I was wondering if there is a way to make a ‘dragable’ snap on to a ‘droppable’ without locking? So even after it snaps on, it can be dragged out of the drop zone. Thanks
@Vivek: Here’s how you prevent the lock:
Awesome david, your blog has been tremendously helpful. However, I was wondering if there a way to undo the inject operation. For example, if I have two droppable R1 and R2 and two dragables G1 and G2; first I insert G1 into R1 (works perfectly with your code update), then I take G1 out of R1 (all good) and then try to put G2 into R1 (G2 appears below R1 instead of inside R1) . I think because of the ‘inject’ it is still inside the parent.
So I was wondering is there a way to ‘eject’ an element from a parent?
Thanks again.
@Vivek: Interesting question. The problem of G2 appearing below R1 could have something to do with the following line:
I’m not certain though. Could you post a sample URL?
“Eject” is a very interesting idea. It would be cool to extend Drag.Move to allow “ejecting” of elements.
Hey David
So the quick fix I’ve got so far, is to re-inject the dragable into the dragable holder with the hack:
This seems to work fine for now, but I still need to test it out.
sorry abou the double post (damn IE)..
figuring out for hours, google and found your side!
this code save me!
THANKS!
Great example.
I’ve been scouring docs.mootools.com and google to find a way to ghost the draggable element, or have it snap back to it’s position to no avail.
I’m basically going for the old Drag.Cart from mootools 1.11 (http://demos111.mootools.net/Drag.Cart). Any where you know to point me?
Ryan
I am also looking for a solution to find a way to ghost the draggable element, or have it snap back to it’s position to no avail. Any one out there has quick example or URL? – saves me lot of time.
hey david, thanks for this:
damn bill gates!
Hello, I think this drag and drop script is perfect for a sidebar gadget i am working on.
I have a few problems with it, perhaps someone here can help…?
First, trying to set it so that the dragable divs are automatically inserted into the holders upon startup. I’ve tried different approaches for this, however, unsuccessful…
Second, my dragable divs contain images, and I am having the darnest time trying to get the images to position themselves correctly. Can anyone help with this? The holder divs position themselves perfectly, however the dragable divs do not. I’ve set the left and top parameters of each image, and the div itself using styles, still can’t get it to position properly.
Can someone please help me?
@David
Where does
position
ever actually get used? I don’t see that the variable is ever referenced again.@AppBeacon: Good catch. Must have been a small snippet left from the testing process. Removed.
hey guys is there a way to save the locations in a databse?
like when your finisht placing the squares you press save?
@Mike : When you drag and drop, the holder gets marked as rel=”filled” . Just create a save button, set an event on the save button to scan for all elements with ref=”filled”. Then, use child selector to get the id of the dragged element inside each droppable. Then, serialize that and submit via AJAX to a web page that saves the values in the db.
wow…lol you lost me..is there any version i can download?i need to see it to understand..i guess im so used to using php and mysql..
@mike
You could also drop an xhr request into the onDrop function. I use this code, not sure if it fits perfectly into Mr. Walsh’s here, but I’m sure you’ll get the idea:
I use different names in my data variables, but you can send information about the droppable and draggable elements to the server script.
i still dont see were i would put this code?and why function(el,droppable)
@mike
Sounds like you need to go through the mootorial, or at least get a bit more familiar with mootools.
I said to put in the onDrop funciton, scroll up to David’s code and you’ll see an “onDrop” function. That’s where :)
el = the element being dragged
droppable = the element receiving
You’d probably need those to give the database anything worthwhile.
I knew nothing about javascript less than a year ago but with mootools I’m able to do all sorts of crap I once thought impossible. It’s just too easy to pick up.
it’s all good thxt i will play arround with it!
Hi David! How can I get the position of the dragable object? I need them to put into a database… Can you help me?
By the way: your site is perfect =)
Thanks :D
LoL vanquybn why don’t you post your whole website and David can sort out all your errors.
By the way for those wanting the ghost drag thing check out this site
http://www.monkeyphysics.com/mootools/script/1/dragghost
Hi David,
Excellent example. In my use case i have the elements which are draggable as well as droppable. so i am having the same class for all the elements.
When we drag an element from top to bottom in forward direction, it’s working fine. In the OnEnter method of Drag.Move, we can find out both draggable element and the entered droppable element.
But the reverse is not happening. Means when we drag any element in reverse direction that is from bottom to top, in the onEnter method i am getting the same element for both draggable and droppable.
i am using css position relative for the div’s.
Can someone help me how to handle this.
Thanks
Visu
Will this work with jQuery?
GREAT Site! You are one of the first sites I read every morning I get going.
Quick Question: Can you force an item to be in a droppable location on page load … if for instance I wanted to load the selections a person made previously via a recordset … and drop their selections where they ended up. Is there a way to do that?
Thanks!
– Patrick