├── LICENSE.txt ├── README.markdown ├── example ├── reorder.html ├── simple.html └── style.css ├── index.html ├── jquery.drag-drop.plugin.js └── jquery.drag-drop.plugin.min.js /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012 Mikael Plate, http://mikeplate.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in the 7 | Software without restriction, including without limitation the rights to use, 8 | copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the 9 | Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Drag and Drop, jQuery Plugin 2 | ============================ 3 | 4 | Overview 5 | -------- 6 | 7 | This is a jQuery plugin for handling drag and drop operations with transparent support for both mouse 8 | and touch events. This means that you will be able to use the exact same code for desktop and mobile 9 | web sites. 10 | 11 | Usage 12 | ----- 13 | 14 | Include the JavaScript file jquery.drag-drop.plugin.js in your html page. 15 | 16 | ~~~~ html 17 | 18 | 19 | ~~~~ 20 | 21 | The plugin is named "dragdrop" and can be applied to an element or a container of elements that should 22 | be draggable. You will probably also specify some options while applying the plugin. 23 | 24 | ~~~~ javascript 25 | $("#area").dragdrop(); 26 | $("#area").dragdrop({ makeClone: true, dragClass: "whileDragging"}); 27 | ~~~~ 28 | 29 | Without any options, the default behaviour is to enable dragging of any elements inside the matched 30 | elements when dragdrop() is called and to enable dropping on any element that has the CSS class 31 | "drop" applied. But by using function callbacks like canDrag and canDrop, you can specify exactly 32 | what can be dragged and where it can be dropped. And you can also specify what actually happens on 33 | a drop with the function callback didDrop. 34 | 35 | If you have an element containing many draggable elements, you can either attach the plugin to 36 | each individual element inside the container or attach it to the single containing element and 37 | then implement canDrag to make sure that the correct child of the container is dragged. 38 | 39 | Options 40 | ------- 41 | 42 | The plugin supports the following options when it is initialized for a source: 43 | 44 | * __makeClone__ can be true or false. Default is false. If true, the actual source element won't be the 45 | element that is dragged but rather a clone of it. 46 | * __sourceClass__ can be the name of a CSS class. This class is applied to the source element 47 | in its original position (if visible) while it is dragged. 48 | * __sourceHide__ can be true or false. When true, the original element is set to invisible while the 49 | dragging occurs. 50 | * __dragClass__ can be the name of a CSS class. If specified, it is applied to the element that is 51 | being dragged while the drag operation is active. Note that if makeClone is false, this is also 52 | the actual source element. 53 | * __canDropClass__ can be the name of a CSS class. If specified, will be applied to the droppable 54 | area element whenever a dragged element is hovering over it, to signify that the user can drop 55 | at this time. 56 | * __dropClass__ can be the name of a CSS class. This class name is used to identify droppable 57 | area elements. The default is "drop". If a callback function is specified under "canDrop", this 58 | class name has no effect. 59 | * __container__ can be a jQuery element of a container. If specified, elements dragged will not be able 60 | to move outside of that container. 61 | * __canDrag__ can be a callback function that returns true or false. You can use this callback if you'd 62 | like to apply the plugin to a larger container, and then only make specific elements inside that 63 | container draggable by returning true from the callback if you've determined the current element 64 | as eligable for dragging. 65 | * __canDrop__ can be a callback function that returns true or false. Return true if the dragged element 66 | can be dropped on the specified element. If this function is used, the "dropClass" setting has 67 | no effect. 68 | * __didDrop__ can be a callback function. If specified, it is assumed to take care of all operations 69 | and effects to occur after a successful drag and drop has been performed. Otherwise, the default 70 | operation is to restore the class on the source element and if makeClone is false the 71 | element will be appended as a child to the droppable element. 72 | 73 | License 74 | ------- 75 | 76 | This software is released under the [MIT License](https://github.com/mikeplate/jquery-drag-drop-plugin/blob/master/LICENSE.txt). 77 | 78 | -------------------------------------------------------------------------------- /example/reorder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Drag and Drop jQuery Plugin Reorder Example 6 | 7 | 8 | 9 | 50 | 51 | 52 |
53 |

Reorder Example

54 |
55 |
56 |

Drag the items below to change their order

57 | 64 |

65 |
66 | Fork me on GitHub 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /example/simple.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Drag and Drop jQuery Plugin Simple Example 6 | 7 | 8 | 9 | 45 | 46 | 47 |
48 |

Simple Example

49 |
50 |
51 |

Drag any of letter boxes to the area below and note some different visuals and rules that can be specified as options and in stylesheets. Read more below about the different plugin customizations that is demonstrated.

52 |
53 |
A
54 |
Bb
55 |
C
56 |
D
57 |
E
58 |
F
59 |
60 |
61 |
G
62 |
H
63 |
I
64 |
J
65 |
K
66 |
L
67 |
68 |
69 |
M
70 |
N
71 |
O
72 |
P
73 |
Q
74 |
R
75 |
76 |
77 |
78 |

A-F shows the default behavior of dragging the original element around.

79 | 82 |

G-L shows custom behavior of dragging a clone of the element and using a different css class for its appearence.

83 | 89 |

M-R shows highlighting the drop area.

90 | 94 |
95 | Fork me on GitHub 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0px; 3 | font-family: Calibri, "Lucida Grande", sans-serif; 4 | background-color: #eee; 5 | } 6 | div.header { 7 | background-color: #aaf; 8 | background-image: -moz-linear-gradient(top, #88d 0%, #88d 50%, #99d 50%, #99d 100%); 9 | background-image: -o-linear-gradient(top, #88d 0%, #88d 50%, #99d 50%, #99d 100%); 10 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #88d), color-stop(0.5, #88d), color-stop(0.5, #99d), color-stop(1, #99d)); 11 | background-size: 4px 4px; 12 | border-bottom: 2px solid #339; 13 | } 14 | h1 { 15 | margin: 0px auto; 16 | max-width: 570px; 17 | padding: 10px 10px 10px 20px; 18 | text-shadow: 1px 1px 0px #bbe; 19 | font-size: 18pt; 20 | } 21 | div.content { 22 | max-width: 560px; 23 | padding: 20px; 24 | margin: auto; 25 | background-image: -moz-linear-gradient(-90deg, #fff 0%, #eee 100%); 26 | background-image: -o-linear-gradient(-90deg, #fff 0%, #eee 100%); 27 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(1, #eee)); 28 | } 29 | h2 { 30 | margin: 0px 0px 15px 0px; 31 | font-size: 16pt; 32 | } 33 | li { 34 | font-size: 14pt; 35 | } 36 | p { 37 | margin: 0px 0px 20px 0px; 38 | font-size: 14pt; 39 | } 40 | .source { 41 | width: 360px; 42 | height: 60px; 43 | margin: auto; 44 | } 45 | .boxes div { 46 | float: left; 47 | width: 60px; 48 | height: 50px; 49 | background-color: #bbe; 50 | background-image: -moz-linear-gradient(-45deg, #eef, #88d); 51 | background-image: -o-linear-gradient(-45deg, #eef, #88d); 52 | background-image: -webkit-gradient(linear, left top, right bottom, color-stop(0, #eef), color-stop(1, #88d)); 53 | font-size: 24pt; 54 | font-weight: bold; 55 | text-align: center; 56 | padding-top: 10px; 57 | -moz-user-select: none; 58 | -webkit-user-select: none; 59 | } 60 | .source div { 61 | cursor: pointer; 62 | } 63 | div.whileDragging { 64 | background-image: none; 65 | background-color: black; 66 | color: white; 67 | } 68 | div.not { 69 | background-image: none; 70 | background-color: #eef; 71 | cursor: inherit; 72 | } 73 | div.pendingDrop { 74 | opacity: 0.5; 75 | } 76 | .drop { 77 | width: 360px; 78 | height: 120px; 79 | overflow: auto; 80 | margin: auto; 81 | margin-top: 25px; 82 | margin-bottom: 25px; 83 | background-color: #888; 84 | border: solid 1px #333; 85 | box-shadow: 2px 2px 2px #666; 86 | -webkit-box-shadow: 2px 2px 2px #666; 87 | text-align: center; 88 | font-size: 24pt; 89 | font-weight: bold; 90 | } 91 | .highlight { 92 | background-color: #dd8; 93 | } 94 | ul#list { 95 | padding: 0px; 96 | } 97 | ul#list li { 98 | list-style-type: none; 99 | padding: 10px; 100 | background-color: #aaf; 101 | background-image: -moz-linear-gradient(-90deg, #eef, #88d); 102 | background-image: -o-linear-gradient(-90deg, #eef, #88d); 103 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #eef), color-stop(1, #88d)); 104 | border-bottom: 1px solid #88d; 105 | font-weight: bold; 106 | cursor: pointer; 107 | } 108 | 109 | 110 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Drag and Drop jQuery Plugin 6 | 7 | 8 | 9 | 10 |
11 |

Drag and Drop jQuery Plugin

12 |
13 |
14 |

About

15 |

This jQuery plugin can handle both mouse- and touch-based drag and drop operations transparently. Its goal is to be simple to use without a lot of options. It is heavily based on style sheet class names and JavaScript callbacks for its customization.

16 |

Download and documentation on Github

17 |

Examples

18 | 22 |
23 | Fork me on GitHub 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jquery.drag-drop.plugin.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var defaultOptions = { 3 | makeClone: false, // Drag a clone of the source, and not the actual source element 4 | sourceClass: null, // Class to apply to source element when dragging a clone of the source element 5 | sourceHide: false, // Specify with true that the source element should hade visibility:hidden while dragging a clone 6 | dragClass: null, // Class to apply to the element that is dragged 7 | canDropClass: null, // Class to apply to the dragged element when dropping is possible 8 | dropClass: null, 9 | isActive: true, 10 | container: null, // if set, dragging is limited to this container 11 | 12 | // Default is to allow all elements to be dragged 13 | canDrag: function($src, event) { 14 | return $src; 15 | }, 16 | 17 | // Default is to allow dropping inside elements with css stylesheet "drop" 18 | canDrop: function($dst) { 19 | return $dst.hasClass("drop") || $dst.parents(".drop").size()>0; 20 | }, 21 | 22 | // Default is to move the element in the DOM and insert it into the element where it is dropped 23 | didDrop: function($src, $dst) { 24 | $src.appendTo($dst); 25 | } 26 | }; 27 | 28 | // Status during a drag-and-drop operation. Only one such operation can be in progress at any given time. 29 | var $sourceElement = null; // Element that user wanted to drag 30 | var $activeElement = null; // Element that is shown moving around during drag operation 31 | var $destElement = null; // Element currently highlighted as possible drop destination 32 | var dragOffsetX, dragOffsetY; // Position difference from drag-point to active elements left top corner 33 | var limits; 34 | 35 | // Private helper methods 36 | 37 | function cancelDestElement(options) { 38 | if ($destElement!=null) { 39 | if (options.dropClass) 40 | $destElement.removeClass(options.dropClass); 41 | $destElement = null; 42 | } 43 | if ($activeElement!=null) { 44 | if (options.canDropClass) { 45 | $activeElement.removeClass(options.canDropClass); 46 | } 47 | } 48 | } 49 | 50 | // Public methods 51 | 52 | var methods = { 53 | init: function(options) { 54 | options = $.extend({}, defaultOptions, options); 55 | this.data("options", options); 56 | this.bind("mousedown.dragdrop touchstart.dragdrop", methods.onStart); 57 | 58 | return this; 59 | }, 60 | 61 | destroy: function() { 62 | this.unbind("mousedown.dragdrop touchstart.dragdrop"); 63 | return this; 64 | }, 65 | on: function() { 66 | this.data("options").isActive = true; 67 | }, 68 | off: function() { 69 | this.data("options").isActive = false; 70 | }, 71 | 72 | onStart: function(event) { 73 | var $me = $(this); 74 | var options = $me.data("options"); 75 | if (!options.isActive) 76 | return; 77 | 78 | var $element = options.canDrag($me, event); 79 | if ($element) { 80 | $sourceElement = $element; 81 | var offset = $sourceElement.offset(); 82 | var width = $sourceElement.width(); 83 | var height = $sourceElement.height(); 84 | if (event.type=="touchstart") { 85 | dragOffsetX = event.originalEvent.touches[0].clientX - offset.left; 86 | dragOffsetY = event.originalEvent.touches[0].clientY - offset.top; 87 | } 88 | else { 89 | dragOffsetX = event.pageX - offset.left; 90 | dragOffsetY = event.pageY - offset.top; 91 | } 92 | 93 | if (options.makeClone) { 94 | $activeElement = $sourceElement.clone(false); 95 | 96 | // Elements that are cloned and dragged around are added to the parent in order 97 | // to get any cascading styles applied. 98 | $activeElement.appendTo($element.parent()); 99 | if (options.sourceClass) 100 | $sourceElement.addClass(options.sourceClass); 101 | else if (options.sourceHide) 102 | $sourceElement.css("visibility", "hidden"); 103 | } 104 | else { 105 | $activeElement = $sourceElement; 106 | } 107 | 108 | $activeElement.css({ 109 | position: "absolute", 110 | left: offset.left, 111 | top: offset.top, 112 | width: width, 113 | height: height 114 | }); 115 | if (options.dragClass) 116 | $activeElement.addClass(options.dragClass); 117 | 118 | var $c = options.container; 119 | if ($c) { 120 | var offset = $c.offset(); 121 | limits = { 122 | minX: offset.left, 123 | minY: offset.top, 124 | maxX: offset.left + $c.outerWidth() - $element.outerWidth(), 125 | maxY: offset.top + $c.outerHeight() - $element.outerHeight() 126 | }; 127 | } 128 | 129 | $(window) 130 | .bind("mousemove.dragdrop touchmove.dragdrop", { source: $me }, methods.onMove) 131 | .bind("mouseup.dragdrop touchend.dragdrop", { source: $me }, methods.onEnd); 132 | 133 | event.stopPropagation(); 134 | return false; 135 | } 136 | }, 137 | 138 | onMove: function(event) { 139 | if (!$activeElement) 140 | return; 141 | 142 | var $me = event.data.source; 143 | var options = $me.data("options"); 144 | var posX, posY; 145 | if (event.type=="touchmove") { 146 | posX = event.originalEvent.touches[0].clientX; 147 | posY = event.originalEvent.touches[0].clientY; 148 | } 149 | else { 150 | posX = event.pageX; 151 | posY = event.pageY; 152 | } 153 | $activeElement.css("display", "none"); 154 | var destElement = document.elementFromPoint( 155 | posX - document.documentElement.scrollLeft - document.body.scrollLeft, 156 | posY - document.documentElement.scrollTop - document.body.scrollTop 157 | ); 158 | $activeElement.css("display", ""); 159 | posX -= dragOffsetX; 160 | posY -= dragOffsetY; 161 | if (limits) { 162 | posX = Math.min(Math.max(posX, limits.minX), limits.maxX); 163 | posY = Math.min(Math.max(posY, limits.minY), limits.maxY); 164 | } 165 | $activeElement.css({ left: posX, top: posY }); 166 | 167 | if (destElement) { 168 | if ($destElement==null || $destElement.get(0)!=destElement) { 169 | var $possibleDestElement = $(destElement); 170 | if (options.canDrop($possibleDestElement)) { 171 | if (options.dropClass) { 172 | if ($destElement!=null) 173 | $destElement.removeClass(options.dropClass); 174 | $possibleDestElement.addClass(options.dropClass); 175 | } 176 | if (options.canDropClass) { 177 | $activeElement.addClass(options.canDropClass); 178 | } 179 | $destElement = $possibleDestElement; 180 | } 181 | else if ($destElement!=null) { 182 | cancelDestElement(options); 183 | } 184 | } 185 | } 186 | else if ($destElement!=null) { 187 | cancelDestElement(options); 188 | } 189 | 190 | event.stopPropagation(); 191 | return false; 192 | }, 193 | 194 | onEnd: function(event) { 195 | if (!$activeElement) 196 | return; 197 | 198 | var $me = event.data.source; 199 | var options = $me.data("options"); 200 | if ($destElement) { 201 | options.didDrop($sourceElement, $destElement); 202 | } 203 | cancelDestElement(options); 204 | 205 | if (options.makeClone) { 206 | $activeElement.remove(); 207 | if (options.sourceClass) 208 | $sourceElement.removeClass(options.sourceClass); 209 | else if (options.sourceHide) 210 | $sourceElement.css("visibility", "visible"); 211 | } 212 | else { 213 | $activeElement.css("position", "static"); 214 | $activeElement.css("width", ""); 215 | $activeElement.css("height", ""); 216 | if (options.dragClass) 217 | $activeElement.removeClass(options.dragClass); 218 | } 219 | 220 | $(window).unbind("mousemove.dragdrop touchmove.dragdrop"); 221 | $(window).unbind("mouseup.dragdrop touchend.dragdrop"); 222 | $sourceElement = $activeElement = limits = null; 223 | } 224 | }; 225 | 226 | $.fn.dragdrop = function(method) { 227 | if (methods[method]) { 228 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 229 | } 230 | else if (typeof method === 'object' || !method) { 231 | return methods.init.apply(this, arguments); 232 | } 233 | else { 234 | $.error('Method '+method+' does not exist on jQuery.dragdrop'); 235 | } 236 | }; 237 | })(jQuery); 238 | 239 | -------------------------------------------------------------------------------- /jquery.drag-drop.plugin.min.js: -------------------------------------------------------------------------------- 1 | (function(i){function l(a){null!=e&&(a.dropClass&&e.removeClass(a.dropClass),e=null);null!=c&&a.canDropClass&&c.removeClass(a.canDropClass)}var o={makeClone:!1,sourceClass:null,sourceHide:!1,dragClass:null,canDropClass:null,dropClass:null,isActive:!0,container:null,canDrag:function(a){return a},canDrop:function(a){return a.hasClass("drop")||0