Mootools Drag and Drop Example

This example is outdated and will not work with Mootools 1.0.

So, without an actual demo to play around with, I thought I'd put one together, both to see how easy it'd be to work with and to help demonstrate to others what to expect. What a better example than the drag and drop (especially since I'm finishing up a project that just happened to use drag and drop). Check out the example and check out the source code. I'll be going through and highlighting some of the interesting things.

Doing just enough

One of the things that I noticed in comparison to the Prototype/Scriptaculous (P&S) approach is that there's a basic framework for handling drag and drop and not much more. Where P&S can do much of it for you with just a couple settings tweaks, Mootools creates the hooks and leaves you to pull off the nitty gritty. Right off the bat, I created my new draggable.

$('dragger').addClassName('drag').makeDraggable();

If you've used Prototype, the dollar function should look familiar. As does the addClassName method. Finally, I can make an item draggable using the makeDraggable method. This demonstrates the chainable methods but also the path to which an item is made draggable. So far so good. But wait, my item isn't actually moving. I added position:relative for my dragger and voila, it's draggable! This is the first of many places where Mootools leaves the details to you.

As a quick sidenote, you can also make an item resizable by using the makeResizable method which uses the same Drag class that is behind makeDraggable. All it does is modify the width and height instead of the left and top properties.

Draggable options

The makeDraggable method can take an options object. Options include callbacks for onStart, onComplete, onDrag, and onSnap. It also accepts an array of droppables which I'll get to in a moment.

var draggableOptions = {
    onStart:function()
    {
      this.el.setOpacity(.5);
    },
    onComplete:function()
    {
      // put the element back where it belongs
      this.el.setOpacity(1);
      this.el.style.left = 0;
      this.el.style.top = 0;
    }
};

// use your own addEvent instead of window.onload
window.onload = function(){
   $('dragger').addClassName('drag').makeDraggable(draggableOptions);
}

With this draggable options object, I change the opacity of the element to 50% when the dragging starts, and set it back to 100%, along with resetting the left and top values to 0. This essentially accomplishes two things Scriptaculous would do for you: ghosting and revert. this refers to the drag object and el is the HTML object with which the effect is attached to. For those that have used Moo.fx, this may be familiar.

Droppables

Honestly, drag and drop isn't much fun unless you have droppables. Next, I added the HTML for the droppables. Before hooking in the droppable JavaScript, I noticed that the dragging would drag under the new HTML. Once again, mootools leaves it to you to decide how this stuff should be styled. A quick z-index on the dragger fixes this right up. Now, onto the JavaScript.

Once again, Mootools differs from Scriptaculous in that there is no Droppable class. But there is a droppable array that is available to us. Simply populate it with a number of elements that you'd like to have as droppables.

var dropitems = document.getElementsBySelector('.item');
draggableOptions.droppables = dropitems;

Two things to mention here: the first is the getElementsBySelector method that is available to use through mootools. It does just like it says and is available through document or any element retrieved via $(). The second is that I've simply attached my array of items to my draggable options object. As droppables though, they're only useful if they can respond to a number of events. Available to us are onOver, onLeave, and onDrop.

var droppableOptions = {
    onOver:function(){
        this.addClassName('dragover');
    },
    onLeave:function(){
      this.removeClassName('dragover');
    },
    onDrop:function(){
      this.addClassName('dropped');
      this.innerHTML = 'dropped';
    }
}

I created a droppable options object that just changes the class names for the most part but on drop also changes the HTML. Now to attach these methods to each of our droppables.

var dropitems = document.getElementsBySelector('.item');
dropitems.each(function(drop){ drop.extend(droppableOptions)   });
draggableOptions.droppables = dropitems;

I use the extend method to add the properties of the options object onto each of the drop items.

All done

Check out the example, if you haven't already, and take a look at the source code to see it all together. I haven't done any cross-browser testing so I'd be interested to hear how well this works in your browser.

In the end, how does this compare to Prototype and Scriptaculous? Without paring down the source code, P&S ranks in at over 100k to pull off drag and drop. Mootools on the other hand accomplished it in 17k. (Values are using the uncompressed versions of the source). And so far, I really like the Mootools approach to things. I think we can expect some cool things from this.

Mootools is available from mootools.net. If you like this, feel free to Digg it.

Published September 11, 2006 · Updated September 14, 2006
Categorized as JavaScript
Short URL: http://snook.ca/s/667

Conversation

48 Comments · RSS feed
Bruno Figueiredo said on September 11, 2006

It work successfully on IE6, Firefox 1.5.0.6, Opera 9.
On IE7, it gives a error, but works.

Cory Hudson said on September 11, 2006

I am absolutely loving the Mootools approach to things. I've already used moo.fx before and enoyed the simplicity and small filesize. Defining a class is much more intuitive with Mootools.

Leaving you to define your own options for draggables can be good or bad. Personally, I think it's the simplest way to go. Script.aculo.us does a good job, but it tries to anticipate every option you'd want to use.

I love Prototype, but it's starting to get too bloated to suit my needs. I think I'll be making the switch to Mootools. The modularity of the library and customizable download is definitely the way to go.

Joel Moss said on September 11, 2006

I think I REALLY like the way MooTools approaches things. I may have to reconsider my usage of P&S in my apps.

Ryan Campbell said on September 11, 2006

Thanks for taking the time to put this together. It made it easier to see the differences between the libraries.

Fredrik Wärnsberg said on September 11, 2006

Very nice exampel right there. I'm trying to learn javascript at the moment, have you got any good [online] references (all the money i can spare for litterature goes to uni-related stuff) for learning javascript?

Regarding the post; I think that many people are ready to make the switch. I think that a part of the wide adoption of prototype is the powerful Rails, Prototype and Scriptaculous combo, but people are also starting to realize that Prototype includes way more than they are in use of, only making page-loads suffer.

ix said on September 11, 2006

fredrik: when i was trying to find the latest prototype.js to download, i found a version that had been split up into the components, each a 2 or 3K JS file. it might be on the rails SVN server somewhere.

Fredrik Wärnsberg said on September 11, 2006

Oh yeah, prototypelite or whatever it's called. That's very lightweight indeed, (about 3kb, correct me if im wrong) but I assume you miss out of LOTS of prototype features with that. With Moo you seem to miss out on less, but customize it more yourself. I don't know though since I dont know either.

Dirk Ginader said on September 12, 2006

Trackback
http://blog.ginader.de/archives/2006/09/12/Die-erste-MOOTOOLS-Demo.php
Valerio Proietti hat sich mit der Veröffentlich seiner MOOTOOLS so verausgabt, daß es leider noch nicht für, die so wichtigen, Demos gereicht hat.

Diesem Missstand hat sich gestern Jonathan Snook angenommen. In dem zugehörigen Blogpost erklärt...

Bramus! said on September 12, 2006

Very nice example Jonathan! Saves us the time of trying to make it all work and points out the essential parts (viz. the "position: relative;")

As some are mentionning here, I do like the way of creating new classes and extending classes too. It feels more genuine when one is used to actual programming (js still is scripting).

MicroAngelo said on September 12, 2006

Looks very interesting, something I'll have to investigate further. I like the idea of having to do a little more work but getting more flexibility, although things like having to set the "position: relative;" on an object that has already had "makeDraggable()" applied to it seems a bit mad.

I've been playing with YUI as an alternative to "P&S" and I like it so far. One thing which has caught my eye is the ease of Targetable affordance (where you can limit drop targets for specific draggables) as in this YUI demo. Any idea how hard this would be to do in MooTools? At first glance the design choice to have a single "droppables" array would seem to preclude this functionality...

Jonathan Snook said on September 12, 2006

Fredrik: Prototype lite was only the code necessary to pull off the effects which certainly meant it left out a lot of cool code. Mootools does a decent job of keeping it lightweight.

MicroAngelo: the single droppables array is per draggable item. Which means that you could define a different set of droppables for each draggable. You could even create different onDrop methods (or have some forking within a single onDrop method) that could behave differently depending on which droppable the draggable was over.

What isn't clear is what happens if you have one droppable for more than one draggable and how the different onDrop states would be handled, since the onDrop is applied directly to the element and not to a droppable object like it is in Scriptaculous. I suppose one could devise a droppable object just for this purpose. And ultimately, that's the flexibility of mootools in that you can choose simple or complex without suffering the size of a large codebase.

Kalle said on September 12, 2006

Really awesome example! I "stole" your example Javascript and made an example of my own. You'll find the related blog post over at my blog and the example's there as well. I really don't hope you'll hate me for doing this. If you'd like me to remove the example or go code my own I will gladly do so, but your example was a great base for me to build on since I'm not that great at Javascript.

Many thanks!

Cory Hudson said on September 12, 2006

Great example, Jonathan. I've written a post about the beautiful ways to extend mootools. I'll be writing some more posts on the library's cool new features.

Luiz Júnior Fernandes said on September 13, 2006

Yeah..., now we only need more examples that make we understand so much better this wonderful new "old" world =]

Bruno Figueiredo said on September 14, 2006

I've posted a sample how how to create a simple lightBox example using the new mooTools.

Wilfred Nas said on September 14, 2006

first of all thanx for a great tutorial...

I also have decided to add my two cents, by editing the script to use css classes instead of opacity in the js.

//this.el.setOpacity(.5); // snook
this.el.addClassName('dragged'); // wnas

Check out the post at my site.

Dustin Diaz said on September 14, 2006

It's interesting what folks expect now from a quality Drag and Drop API. The essential "interesting moments" are becoming more standardized such as "startDrag, endDrag, onDrag, onDragOver, onDragOut, onDragDrop..." etc. It seems like Moo has done a good job at delivering that :)

MikeB said on September 17, 2006

Well as others have said, great example (I'm in the process of breaking down some examples myself and will post them as soon as I've finished (already got a Resize, ResizeWidth only, ResizeHeight only in place).

One question though, anyone know how to make an object UNdraggable, once you've made it draggable?

Thanks
Mike B.

Jonathan Snook said on September 17, 2006

MikeB: there's no "undrag" feature but a quick test that serves its purpose is to remove the onmousedown event from the draggable. You can do this either via the onComplete of the draggable event using this.el.onmousedown = null or in the onDrop event of the droppable.

onDrop:function(o){o.onmousedown = null}

The o object is the draggable div. It gets passed in as the first parameter when onDrop fires. Therefore, just nullify the onmousedown and voila, you can no longer drag.

Mike B. said on September 18, 2006

Kewel, thanks Jonathan for such a quick response. I kinda figured that there was no "undrag" when looking at the code but wasn't sure if I missed something or not and the thought of just "nulling" out the onmousedown was a bit disturbing from a memory leak perspective (since in the code I'm putting together I want to add back the "draggable" ability at a later time to the objects. But I'll give it a try and see what happens.

Thanks again
Mike B.

mike kidder said on September 18, 2006

I have attempted to try this example against the latest SVN drop (revision 60) and seems something has changed. The "this" keyword context in the drop target events for "droppableOptions" no longer references the element, but the draggable Object. Needless to say, this causes the addClassName, removeClassName to throw out errors. Has anyone had the same result?

Valerio Proietti said on September 19, 2006

@Jonathan Snook && MikeB: I have added dragObject.pause and resume (thanks for the suggestion!).
And now the object is passed as the second argument. for example you could do something like this:


$('myDropElement').onDrop = function(elementDropped, dropObject){
elementDropped.setOpacity(0.5);
dropObject.pause();
}

This way is also easier to manage the "one droppable/many draggable" scenario.

@mike kidder: in the new revision I have fixed this. I overlooked the problem, and I agree that the this keyword should refer to the current element for the onDrop/onLeave/onOver events.

Bruno Figueiredo said on September 22, 2006

Hi all. I've added a new sample using the mooTools. This is a port of the reflector effect. It's amazing how it was easy to do.

@Valerio: The only problems I had was with the getBrother, because it fetches elements on not the real next/previous sibling. An additional parameter to this function would be great, one that could tell if we wanted the elements or non elements to be found.

logan said on October 04, 2006

Great, thank you for this sample.
I am a beginner user of mootools and I have a lot of questions about their use, but...
only one thing, Is there something like prototype's 'Event.observe()' in mootools framework?
Thanks!!!!

Bundyo said on October 04, 2006

Hi ppl. I've made an extended Drag&Drop example with a Drag element class, which allows multiple drag elements (and multiple drag containers) to be used. Right now it has only two containers, but easily you can use say five containers.

I also wrote a small article about it, but it's in Bulgarian so i won't share it with you :P

The example only is here:
http://www.bundyo.org/mootools/

valar said on October 12, 2006

Nice, and sorry it this comment it's off-topic but your design looks different in FF and IE.

Greetings and thanks for the article for mootools!

Carlos R. del Moral said on October 12, 2006

It's works ok on FreeBSD 5.4 via Mozilla Firefox 1.0.3

carlos again said on October 12, 2006

Opera 8.51 / FreeBSD 5.4
-----
A little error the first time that select the item to drag, it's appear that TOP+=SIZE OF ITEM , but the rest its ok.

absynthe said on October 14, 2006

A better example of 2-dimensional sortable list (multi columns and rows) based this time upon the Sortables class from Mootools. I rewrited it to take care of the x axis. You can find the example here :
http://chatalors.chez-alice.fr/sortables.html

It works perfect in Firefox and seams perfect in IE too, but you should read my post here about possible closures firing problems with IE on refreshing/reloading page :
http://freewebsfarms.com/forums/viewtopic.php?pid=3029#p3029

Mike B. said on October 26, 2006

Valerio, I saw your comments in entry # 22 (Thanks for the suggestion), however did you implement this in the mootools code or is this something I need to add to my code (can't find the "pause" or "resume" in the current (e.g. R77) or even latest SVN (R82) of the code.

Thanks again
Mike B.

Mike B. said on October 26, 2006

Oh and not to be greedy <grin> but can the pause/resume be done w/o a "dropObject"? In the code I'm working on I'm not "dropping" the item being dragged into a specific container, just dragging it around the screeen for positioning (sort of like the "mooglets" example). although I suppose I could use the Body element as a "dropObject" now couldn't I?

Mike B.

raju said on November 03, 2006

The drag and drop is very useful which resent here.It is really amazing.
But my requirements is to make two frames and provide drag and drop component.It should be just like add playlist in media player.
Please give sugesstion by giving code for dragging elements betwwen two frames.

-Raju

Jonathan Snook said on November 05, 2006

raju: doing a drag and drop across frames won't really be possible with any of the existing libraries (I suspect). It's probably best to build something like this from scratch. You'll need the two documents to be able to communicate between each other to track whether a drag event has started in one document as the mouse enters the other.

Temuri said on December 09, 2006

Nice demo, clean and simple.

The question is - how to introduce a drag proxies, in the way YUI does it?

Thanks,
Temuri

Jonathan Snook said on December 10, 2006

Temuri: Unfortunately, drag proxies aren't really possible with the mootools library in its current incarnation. You'd have to rewrite the initialization method and the addStyles method to tie in a proxy element. My thought is that you'd probably be better off to stick with YUI or Scriptaculous.

MooNoob said on December 14, 2006

You can do Proxies. Just add this to your onStart function.:

this.element = this.element.clone().injectInside(document.body);

MooNoob said on December 14, 2006

Sory, forgot some stuff

this.elementOrg = this.element;
this.element = this.element.clone().setStyles({
position: 'absolute',
top: this.element.getTop() + 'px',
left: this.element.getLeft() + 'px',
opacity: '0.6'
}).injectInside(document.body);

raypov said on December 15, 2006

Drag & Drop swap positions example?
Have seen alot of examples of drag and drop sortable lists, but no examples where the dragged object swaps positions with the object it is dropped onto.
If your trying to reorder images, it would be nice if all the other images didn't start sliding around and reordering as soon as you start dragging. Kind of counterintuitive.

wishnu said on December 15, 2006

Hello, great tutorial.
but i cant get it to work on IE, i copy and paste your example page and edited the js to load my mootools js ( ver 83 ).
and firefox keep generating error on line 22 and 27 ( this.el.setOpacity )

wishnu said on December 15, 2006

problem solved :)
just add top and left style for the dragger

Oliver Treend said on December 20, 2006

I really like Mootools, but I can't seem to find any clear and descriptive documentation on it, so I am really glad you made this page!

I am having trouble with IE and Opera because most of the time Mootools only works in Firefox!!

I would like to see a lot more documentation on the graphical effects and transitions Mootools has to offer, because I can't seem to get any to work.

If anybody finds any good tutorials or documentation on Mootools that you think I may have missed, feel free to email me: info [at] olivertreend [dot] com. Thanks!

jov said on December 22, 2006

nice one

kishore said on February 08, 2007

normally if we have a big list we need to scroll. but it is not working while dragging.

James Chen said on February 28, 2007

You might want to let your readers know that this example is out of date and will no longer work with mootools v1.0. The draggables code can be made towork pretty easily by changing this.el to this.element.

However, due to the new fireEvent used in the Drag.Base, you'll have to register the onOver, onLeave, onDrop events separately rather than simply extending them.

So instead of drop.extend(droppableOptions), do:

drop.addEvents(droppableOptions);

And you'll also have to change 'onOver' to 'over'.

I see this as a bug in the Drag.Base implementation, and I have filed it accordingly. So this will probably be just a temporary fix.

Cheers,
James

Ergün KOÇAK said on March 29, 2007

to James Chen

tnx very very much for update i lost hours to find this out

regards

Othon said on June 18, 2007

First, It's great, works perfectly on IE 6 and Firefox.
Second, i edit the html and a combo next to the text Item 1 and, when dragging the div it doesn´t stay over the combo. I know this is a IE bug and wahtwvere but, is ther a way to work around with this bug?.

Greetings

Joseph Russell said on September 16, 2007

Very useful example here. I recently found your blog as I've been teaching myself cakephp in the last few weeks. I'm impressed.

I like the amount that mootools does for you. Its lightweight and easy to extend it to do what you need.

Shane said on January 22, 2009

Thanks for the demo, its cool (there's a javascript error though when you start to drag in your sample).

Someone mentioned swapping drag and drops? I saw this on YouTube a about a month ago and asked the guy about how he did it. He said he used Mootools but he wouldn't let me have the code [yet]. It's worth looking at because the example shows that he drags and drops before, after and even swaps with other elements.

Looks cool!
http://nl.youtube.com/watch?v=pvvmqP89lb8

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.