MooTools Drag.Ghost

Version 1.01
Ghosting draggable extension for Drag.Move
Show me some demo's

License

You can freely use this work - but some rights are reserved.

Downloads

There are two downloads available, the original source file and a minified version ready for deployment. Most likely you will just want to use the script without looking at its internals - get the minified version.
» Minified (for production)
» Source code (for development)

Usage

Instance
new Drag.Ghost(element [, options]);
Element function
Element.makeGhostDraggable([options]);

Styling

Be aware that the original Drag class from MooTools will default to position: absolute and a certain left and right when not set. This might result in undesired rendering of your draggables, so you're recommended to set these styles for draggables. I've just set them to:
position: relative;
top: 0;
left: 0;

Options

OptionDefault valueDescription
opacity0.65a value between 0 and 1 defining the opacity of the dragging ghost

in addition to all Drag and Drag.Move options.

Requirements

  • MooTools Core v1.2.x
  • Drag and Drag.Move classes from MooTools More

Examples

Simple


Drag Me #1
Drag Me #2
Drag Me #3
Drag Me #4
Drag Me #5
Drag the blue boxes onto the pink droppables:
Droppable #1
Droppable #2
Droppable #3
Dragging should work perfectly as intended.

» view code

Javascript

window.addEvent('load', function() {
	$$('.drag').makeGhostDraggable({
		droppables: $$('.drop'),
		onDrop: function(element, droppable) {
			if (droppable) {
				droppable.set('html',
					droppable.get('html')
					+ '<br/>' 
					+ element.get('html')
				);
			}
		}
	});
});

HTML

<div class='drag'>Drag Me #1</div>
<div class='drag'>Drag Me #2</div>
<div class='drag'>Drag Me #3</div>
<div class='drag'>Drag Me #4</div>
<div class='drag'>Drag Me #5</div>

<div class='clear'>Drag the blue boxes onto the pink droppables:</div>

<div class='drop'>Droppable #1</div>
<div class='drop'>Droppable #2</div>
<div class='drop'>Droppable #3</div>

<div class='clear'>Dragging should work perfectly as intended.</div>

CSS

.drag {
	position: relative;
	top: 0px;
	left: 0px;
	float: left;
	background: #0af;
	border: 1px solid #000;
	padding: 10px;
	cursor: move;
	width: 50px;
	text-align: center;
	margin: 0 20px 0 0;
}

.drop {
	float: left;
	background: #f0a;
	border: 1px solid #000;
	padding: 10px;
	color: #fff;
	text-align: center;
	margin: 0 20px 0 0;
}

.clear {
	clear: both;
	padding: 15px 0;
}
 

Add comment

Comments (16)

19-03-2009, 12:02
Theo
The drag function doens't work in IE. Am I doing something wrong? Your version works perfect, but when I copy -> paste it all went wrong.
19-03-2009, 16:18
Monkey
Can you be more specific? What went wrong? Pay attention to the section above dealing with "Styling", maybe that helps?
24-04-2009, 15:20
stdu
Hi,
I'm trying to use your class work fine but I need to drag img with href
Is it possible to add a delay to allow the click to open the current link ?
Sorry, I tried but I don't know enough mootools.
Big Thx
24-04-2009, 23:25
Monkey
A link is for clicking, a draggable for dragging. The two mouse-actions should not go together. I'd say you increase the size of you draggable to have plenty of space outside the link to drag it by.
09-06-2009, 02:33
lbljeffmo
@Monkey: At first it seems complicated or highly error-prone to attempt to distinguish between a link-click and a drag-click...but I found that it's actually not so difficult to accomplish:

In the onDrop method, just check the overall distance of the drag. If the distance is greater than a (very small...1-2px) margin of error, dragging events are fired and the onClick action returns false (preventing the linking action in the browser). Otherwise the custom onDrop event is not fired...and instead returns true to execute the browser linking action.

So the margin of error accounts for any accidental drag on the part of the user when clicking the link. (In fact, while testing things out myself, I found that I almost never had ANY accidental drag...but its always a good idea to account for all the coffee addicts and grandmas of the net)

Anyway this gave me a very intuitive draggable links feature with zero false-positives (during testing atleast) in my mootools imp, so I thought I'd throw it out there.
26-10-2009, 21:11
Brian
Thanks for posting this. I'd been struggling with mootools drag setting position to 'absolute' and didn't think I could solve it until I saw this page.
28-11-2009, 17:41
damine
It's magical, excellent work !
thanks a lot for that code :)
09-12-2009, 00:43
Crispijn
Great class! I've used this solution in a project I'm working on several weeks ago but I've done it manually. I'll review my code and try to implement this sweet little class!

Great job!
05-02-2010, 15:54
kburn
hi,
your script is awsum and it works like a charm, but
I found out that if you place the draggable list into a container that has the overflow:scroll property, it turns out that when you try to drag the elements down in the scroll position, the ghost clone is created in the wrong place, I found the solution, and here is the code:

ghost: function() {
this.element = this.element.clone()
.setStyles({
'opacity': this.options.opacity,
'position': 'absolute',
'z-index': 1000,
'top': ((e.page.y)-(this.element.getStyle('height').toInt()/2)), //this.element.getCoordinates()['top'],
'left': ((e.page.x)-(this.element.getStyle('width').toInt()/2)) //this.element.getCoordinates()['left']
})
.inject(document.body)
.store('parent', this.element);
},


-----


the main changes are into the ghost function.
hope this could be useful to others that encountered this issue.

cheerz

-k-
05-02-2010, 15:57
kburn
sorry, I posted the wrong code,
this is the right one. basically you have to pass the event to the this.gost() function @ line 22, that becomes
this.ghost(event);

and then this:

ghost: function() {
this.element = this.element.clone()
.setStyles({
'opacity': this.options.opacity,
'position': 'absolute',
'z-index': 1000,
'top': ((e.page.y)-(this.element.getStyle('height').toInt()/2)), //this.element.getCoordinates()['top'],
'left': ((e.page.x)-(this.element.getStyle('width').toInt()/2)) //this.element.getCoordinates()['left']
})
.inject(document.body)
.store('parent', this.element);
},

05-02-2010, 15:59
kburn
I\'m deeply sorry I still had the wrong code in my clipboard (fail!)

ghost: function(e) {
this.element = this.element.clone()
.setStyles({
\'opacity\': this.options.opacity,
\'position\': \'absolute\',
\'z-index\': 1000,
\'top\': ((e.page.y)-(this.element.getStyle(\'height\').toInt()/2)), //this.element.getCoordinates()[\'top\'],
\'left\': ((e.page.x)-(this.element.getStyle(\'width\').toInt()/2)) //this.element.getCoordinates()[\'left\']
})
.inject(document.body)
.store(\'parent\', this.element);
},
08-02-2010, 21:18
nate
How can I make it so if I drag an item into one of the boxes it will delete the other item that was in there. Currently it adds it. So if you drag \"Drag Me #1\" into the first box and then drag \"Drag Me #2\" in the first box, \"Drag Me #1\" is removed.

Thanks in advance,

Nate
09-02-2010, 04:41
Gregg
Your examples don't work correctly in ie8 the blue box doesn't drag just the text inside the blue box
23-04-2010, 15:38
Carlos Morcerf
worked very well with a small change::

ghost: function() {
var parent = this.element.getParent();
this.element = this.element.clone()
.setStyles({
'opacity': this.options.opacity,
'position': 'absolute'
})
.inject(parent)
.position({'relativeTo':this.element,'position':'topLeft'})
.store('parent', this.element);
},
10-08-2010, 21:35
atanas
If you need to have a link that's draggable, just use this kind of code:

$$('.drag').each(function(i,x,a){
new Drag.Ghost(i, {

droppables: '.drop',

onDrop: function(element, droppable, event){
if (droppable!=null&&droppable!=false) {
//here you write your events...
}
},

onEnter: function(element, droppable){
},

onLeave: function(element, droppable){
},

onBeforeStart: function(){
this.gofunc= function(){ return true; };
if (this.get('href')){
this.gofunc= function(){
location.assign(this.get('href'));
return false;
};
} else {
var a= this.getElement('a');
if (a!=null&&a.get('href')){
this.gofunc= function(){
location.assign(a.get('href'));
return false;
};
}
}
}.bind(i)
,onDrag: function(){
this.gofunc= function(){ return true; };
}.bind(i)
,onComplete: function(){
return this.gofunc();
}.bind(i)
,onCancel: function(){
return this.gofunc();
}.bind(i)
});

}
);

This is a part of my own function that has to control droppables for a cart- online shop where each product is a link that can be dropped on a cart icon. Sometimes we just need a draggable link(Let's imagine sth. like a destop with icons as a interface too.). The idea is pretty simple- if there is a click(onBeforeStart), we initialize a function to get the first href inside draggable(in my case items are divs with price tag,image, item title) and call it on button release(onCancel,onComplete). When dragged the link inits the function to return true only(this is to be able to call onDrop for the droppable). This works and is tested- you click and don't move more than options.snap and you have a link, you move and drag- you have a draggable. :)
16-08-2010, 20:15
TetoMaggot
Hi!
I worked in a version based in the ExtJs draggable, and modifing the Drag.Ghost script, i created this script to drag a element like a ghost, isn't the same appearence of ExtJs, this version is more quick of ! ;)

Drag.Ghost = new Class({

Extends: Drag.Move,

start: function(event) {
this.ghost();
this.parent(event);
},

cancel: function(event) {
if (event) this.deghost();
this.parent(event);
},

stop: function(event) {
this.deghost();
this.parent(event);
},

ghost: function() {

var el = this.element;

/* To previne IE6 Bugs */
var parent = this.element;

/* Save important data from selected element. To previne IE6 Bugs */
var top = el.getCoordinates()['top'];
var left = el.getCoordinates()['left'];
var height = el.getStyle('height');
var width = el.getStyle('width');

/* Hide the selected element */
this.element.setStyle('opacity' , '0.0');

/* Adding some styles to new 'ghost' item */
this.element = el.clone()
.setStyles({
'opacity': '1.0',
'position': 'absolute',
'top': top,
'left': left,
'border' : '2px dotted #D0D0D0',
'height' : height,
'width' : width
})
.inject(document.body)
.empty()
.set('html','&nbsp;')
.store('parent',parent);
},

deghost: function() {
var e = this.element.retrieve('parent');
var top = this.element.getCoordinates()['top'];
var left = this.element.getCoordinates()['left'];
this.element.destroy();
this.element = e;

/* Set coordenates and some style of a retrieved element */
e.setStyles({
'top': top,
'left': left,
'opacity' : '1.0'
});

}
});

Element.implement({
makeGhostDraggable: function(options) {
return new Drag.Ghost(this, options);
}
});