O'Reilly

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!

Track.js Error Reporting

Recent Features

  • Write Simple, Elegant and Maintainable Media Queries with Sass

    I spent a few months experimenting with different approaches for writing simple, elegant and maintainable media queries with Sass. Each solution had something that I really liked, but I couldn't find one that covered everything I needed to do, so I ventured into creating my...

  • I&#8217;m an Impostor

    This is the hardest thing I've ever had to write, much less admit to myself.  I've written resignation letters from jobs I've loved, I've ended relationships, I've failed at a host of tasks, and let myself down in my life.  All of those feelings were very...

Incredible Demos

  • Camera and Video Control with HTML5

    Client-side APIs on mobile and desktop devices are quickly providing the same APIs.  Of course our mobile devices got access to some of these APIs first, but those APIs are slowly making their way to the desktop.  One of those APIs is the getUserMedia API,...

  • jQuery Link Nudging

    A few weeks back I wrote an article about MooTools Link Nudging, which is essentially a classy, subtle link animation achieved by adding left padding on mouseover and removing it on mouseout. Here's how to do it using jQuery: The jQuery JavaScript It's important to keep...

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

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

Recently on David Walsh Blog

  • Loading Static Templates for Intern Testing

    I use Intern by SitePen for all of my JavaScript functional testing.  Intern has loads of features other functional test frameworks don't and it's completely Promise-based -- something I got very used to when I used the Dojo Toolkit every day. Async test creation can...

  • Convert Video to mp3

    Let's all be honest for a moment:  we've all ... not paid for ... music.  Whether it was via a file sharing app like Kazaa or Napster, or it was downloading and seeding on bittorrent, or maybe even downloading a music video and ripping its audio,...

  • Sort git Branches by Date

    I'll be first person to admit I don't do as much git repository maintenance as I should.  I rarely delete branches which have been merged, so a git branch execution shows me a mile-long list of branches that likely aren't relevant.  The best way to find branches I've recently...

  • Best Tools and Resources for Web Professionals in 2015

    Looking for the right resources to help you satisfy the needs of your clients? On the lookout for the best tools to help you increase your revenue? Searching for the right software to help you improve your business? Well, then you’ve come to the right place....

  • JavaScript Polling

    Polling with JavaScript is one of those ugly but important functions within advanced front-end user experience and testing practices.  Sometimes there isn't the event you can hook into to signify that a given task is complete, so you need to get your hands dirty and simply poll for...