Treehouse

Drag. Drop. Lock.

By on  

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!

ydkjs-4.png

Recent Features

  • Detect DOM Node Insertions with JavaScript and CSS&nbsp;Animations

    I work with an awesome cast of developers at Mozilla, and one of them in Daniel Buchner. Daniel's shared with me an awesome strategy for detecting when nodes have been injected into a parent node without using the deprecated DOM Events API....

  • CSS&nbsp;@supports

    Feature detection via JavaScript is a client side best practice and for all the right reasons, but unfortunately that same functionality hasn't been available within CSS.  What we end up doing is repeating the same properties multiple times with each browser prefix.  Yuck.  Another thing we...

Incredible Demos

  • Create Digg URLs Using&nbsp;PHP

    Digg recently came out with a sweet new feature that allows users to create Tiny Digg URLs which show a Digg banner at the top allowing easy access to vote for the article from the page. While I love visiting Digg every once in a...

  • Translate Content with the Google Translate API and&nbsp;JavaScript

    Note:  For this tutorial, I'm using version1 of the Google Translate API.  A newer REST-based version is available. In an ideal world, all websites would have a feature that allowed the user to translate a website into their native language (or even more ideally, translation would be...

Discussion

  1. Pretty cool stuff! Thanks!

  2. Paul

    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.

  3. @Paul: Awesome catch! I didn’t notice that. Hopefully I’ve have some time to hit that this weekend.

  4. @Paul: I’ve updated the post to disallow dropping more than one DIV in a droppable.

  5. Tim

    Is it anyway possible to lock them entirely so that if you call refresh the
    page the boxes will stay where you droped them ?

  6. @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

  7. Tim

    @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 ?

  8. @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.

  9. Vivek

    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

  10. @Vivek: Here’s how you prevent the lock:

    onDrop: function(el,droppable) {
    //inject into current parent
    el.addClass(‘locked’);
    el.setStyles({‘left’:0,’top’:0,’position’:’relative’,’margin’:0}); //hack
    }
    },

  11. Vivek

    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.

  12. @Vivek: Interesting question. The problem of G2 appearing below R1 could have something to do with the following line:

    drag.setStyles({ ‘top’:0, ‘left’:0, ‘margin-right’:’20px’ });

    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.

  13. Vivek

    Hey David

    So the quick fix I’ve got so far, is to re-inject the dragable into the dragable holder with the hack:

    onDrop: function(el,droppable) {
    if(droppable)
    {
    if(droppable.get(‘rel’) != ‘filled’)
    {
    el.inject(droppable);
    el.addClass(‘locked’);
    el.setStyles({‘left’:0,’top’:0,’position’:’relative’,’margin’:0}); //hack — wtf?
    }
    else
    { //filled

    }
    }
    else
    {//not a drop zone
    el.inject(‘dragable-holder’);
    el.removeClass(‘locked’);
    el.setStyles({‘left’:0,’top’:0,’position’:’relative’,’margin’:0}); //hack — wtf?
    }
    },

    This seems to work fine for now, but I still need to test it out.

  14. Vivek

    sorry abou the double post (damn IE)..

  15. christleo

    figuring out for hours, google and found your side!

    document.ondragstart = function () { return false; }; //IE drag hack

    this code save me!

    THANKS!

  16. Ryan

    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?

  17. RK

    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.

  18. hey david, thanks for this:

    el.setStyles({‘left’:0,’top’:0,’position’:’relative’,’margin’:0}); //hack — wtf?

    damn bill gates!

  19. SoLoGHoST

    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?

  20. @David

    //this is where we’ll save the initial spot of the image
    var position = drag.getPosition();

    Where does “position” ever actually get used? I don’t see that the variable is ever referenced again.

  21. @AppBeacon: Good catch. Must have been a small snippet left from the testing process. Removed.

  22. mike

    hey guys is there a way to save the locations in a databse?

    like when your finisht placing the squares you press save?

  23. @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.

  24. mike

    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..

  25. @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:

    onDrop: function(el,droppable) {
        var req = new Request.HTML({
            method: 'get',
            url: 'some_script.php',
            data: { 'droppable' : droppable.get('rel'), 'draggable' : el.get('rel') },
            update: $$('billing_items_content'), 
          }).send();
    }
    

    I use different names in my data variables, but you can send information about the droppable and draggable elements to the server script.

  26. mike

    i still dont see were i would put this code?and why function(el,droppable)

  27. @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.

  28. mike

    it’s all good thxt i will play arround with it!

  29. 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

  30. Grrreat

    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

  31. visu

    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

  32. Will this work with jQuery?

  33. I just wanted to say thanks for the IE hack in the code above!


    document.ondragstart = function () { return false; }; //IE drag hack

    I wanted to make the draggables have an anchor element only so that the user would be able to use TAB to step to the draggable for keyboard accessibility.


    That works fine in FF but not in IE7. Applying the above hack solves the problem nicely!

    Thanks!

    /Arvind

  34. Patrick

    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

  35. [...] Drag. Drop. Lock.- [...]

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!