76 |
77 | Lean & Mean Drag and Drop is a small script for dragging, dropping, sorting and reordering html structures
80 |Features
81 |Usage
115 |//Load LMDD css and js files
116 | <link href="../css/lmdd.min.css" rel="stylesheet">
117 | <script src="../js/lmdd.min.js"></script>
118 |
119 | //Initialize LMDD instance with your preferred options
120 | <script>lmdd.set(document.getElementById('markupID'),{optionsObject});</script>
121 | Examples
124 |-
125 |
-
126 | speaker_notes127 |128 |135 |
129 | The basic script is essentially the same for all cases. Set LMDD on an element (containing 130 | all that has to be dragged or nested in), and declare the relevant classes 131 | (containerClass,draggableItemClass & handleClass). The rest is done through proper HTML 132 | markup. 133 |
134 |
136 | -
137 | filter_1Simple Grid138 |139 |153 |140 |152 |141 |151 |Green142 |Pink143 |Purple144 |Blue145 |Lime146 |Teal147 |Amber148 |Orange149 |Black150 |
154 |
-
155 | filter_2Multiple containers + handle156 |157 |211 |158 |210 |159 |173 |Tasks160 |161 |166 |162 | reorder 163 |165 |Code refactoring & optimizations164 |167 |172 |168 | reorder 169 |171 |Cross browser testing170 |174 |182 |Important Tasks175 |176 |181 |177 | reorder 178 |180 |Call mom179 |183 |209 |Done184 |185 |190 |186 | reorder 187 |189 |Basic Drag & Drop script188 |191 |196 |192 | reorder 193 |195 |Smooth transitions194 |197 |202 |198 | reorder 199 |201 |Support nested structures200 |203 |208 |204 | reorder 205 |207 |Scroll while dragging206 |
212 |
-
213 | filter_3Nested Grid214 |215 |235 |216 |234 |217 |233 |218 |222 |219 |
Red
220 |Red
221 |Red
223 |227 |224 |Green
225 |Green
226 |Green
228 |232 |229 |Blue
230 |Blue
231 |Blue
236 | -
237 | code238 |239 |292 |
291 |Simple 240 | <div id="basic-example"> (scope) 241 | <div class="grid"> (container) 242 | <div class="item"></div> (draggable) 243 | </div> 244 | </div> 245 | <script> 246 | lmdd.set(document.getElementById('basic-example'), { 247 | containerClass: 'grid', 248 | draggableItemClass: 'item' 249 | }); 250 | </script> 251 | 252 | Multiple Containers and handle 253 | <div id="handle-example"> (scope) 254 | <div class="grid"> (container) 255 | <div class="item"> (draggable) 256 | <div class="handle"></div> (handle) 257 | </div> 258 | </div> 259 | <div class="grid"> (another container) 260 | </div> 261 | </div> 262 | <script> 263 | lmdd.set(document.getElementById('handle-example'), { 264 | containerClass: 'grid', 265 | draggableItemClass: 'item', 266 | handleClass:'handle' 267 | }); 268 | </script> 269 | 270 | Nested 271 | <div id="nested-example"> (scope) 272 | <div class="grid"> (container) 273 | <div class="grid item"> (container AND draggable) 274 | <div class="item"></div> (draggable) 275 | <div class="item"></div> (draggable) 276 | <div class="item"></div> (draggable) 277 | </div> 278 | <div class="grid item"> (container AND draggable) 279 | <div class="item"></div> (draggable) 280 | <div class="item"></div> (draggable) 281 | <div class="item"></div> (draggable) 282 | </div> 283 | </div> 284 | </div> 285 | <script> 286 | lmdd.set(document.getElementById('nested-example'), { 287 | containerClass: 'grid', 288 | draggableItemClass: 'item' 289 | }); 290 | </script>
293 |
Options
297 |-
298 |
-
299 | speaker_notes300 |301 |368 |302 |367 |
303 | 304 |
366 |305 | 308 | 309 | 310 |Option 306 |Description 307 |311 | 314 |containerClass 312 |Containers act as drop zones for draggable items 313 |315 | 318 |draggableItemClass 316 |Draggable items can be moved inside and between containers 317 |319 | 322 |handleClass 320 |Restricts drag start to a specific element which acts as a drag "handle" 321 |323 | 328 |dragstartTimeout 324 |Delays the drag start for a short time to distinct it from other user intentions 325 | (such as selecting text) 326 | 327 |329 | 334 |calcInterval 330 |Time interval in which LMDD evaluates the dragged element position, Short 331 | interval means more responsive experience (and more CPU usage) 332 | 333 |335 | 340 |revert 336 |When set to true the draggable item will revert to its original position when 337 | the cursor is out of the container bounds 338 | 339 |341 | 346 |nativeScroll 342 |LMDD uses its own auto-scroll function, you can eliminate it and use the native 343 | browser scroll 344 | 345 |347 | 352 |mirrorMinHeight, 348 | mirrorMaxWidth 349 | 350 |Scale down the mirror size when it exceeds the maximum width set 351 |353 | 358 |positionDelay 354 |When set to true - position will not be recalculated when the mouse stops moving 355 | (Prevents flickering on deep nested structures) 356 | 357 |359 | 364 | 365 |dataMode 360 |When set to 'true' LMDD will undo all DOM mutations when the drag event ends, 361 | This is useful for integrating LMDD with Angular/React/Vue etc. 362 | 363 |
369 | -
370 | code371 |372 |387 |
386 |//LMDD Options default values: 373 | lmdd.set(document.getElementById('someID'), { 374 | containerClass:'lmdd-container', 375 | draggableItemClass: 'lmdd-draggable', 376 | handleClass: false, 377 | dragstartTimeout: 50, 378 | calcInterval: 200, 379 | revert: true, 380 | nativeScroll: false, 381 | mirrorMinHeight: 100, 382 | mirrorMaxWidth: 500, 383 | positionDelay: false, 384 | dataMode: false 385 | });
388 |
Advanced
392 |-
393 |
-
394 | content_copyCloning395 |396 |414 |
397 | To clone dragged elements, just add two additional classes to your markup:
403 |
398 | 'lmdd-clonner' - for every element you want to clone when dragged
399 | 'lmdd-dispatcher' - for the container (parent of the elements to be cloned)
400 | containers having the 'lmdd-dispatcher' class will not act as drop zones, the cloning 401 | operation will begin when the mouse cursor enters a different container. 402 |
413 |//Example markup: 404 | <div id="clone-example"> (scope) 405 | <div class="grid lmdd-dispatcher"> (dragging items out from this container will clone them) 406 | <div class="item lmdd-clonner">(this draggable item will be cloned when dragged) 407 | </div> 408 | </div> 409 | <div class="grid"> (container) 410 | </div> 411 | </div> 412 |
415 | -
416 | eventEvents417 |418 |445 |
LMDD dispatches three events:
419 |-
420 |
- 'lmddbeforestart': Dispatched before any changes has been made to the 421 | DOM. Does not contain a detail object 422 | 423 |
- 'lmddstart': Dispatched when the drag operation starts. Contains a 424 | 'detail' object 425 | 426 |
- 'lmddend':Dispatched when the drag operation ends. Contains a 427 | 'detail' object 428 | 429 |
The detail object carries the following information:
431 |-
432 |
- dragType: "move" or "clone" 433 |
- draggedElement: HTML element being dragged 434 |
- from.container: HTML element that originally contained the dragged 435 | element 436 | 437 |
- from.index: The original index of the dragged element 438 |
- to.container: HTML element that currently contains the dragged 439 | element 440 | 441 |
- to.index: The current index of the dragged element 442 |
444 |
446 | -
447 | blockRendering Blocks448 |449 |468 |
450 | To animate transitions LMDD makes a copy of all movable elements on its scope and manage 451 | their location with relative positioning,
459 |
452 | this is an expensive process and might (in some cases) alter the way your markup renders 453 | while dragging.
454 | To prevent this from happening, AND save CPU on drag operations, add the class 'lmdd-block' 455 | to the parts of your markup which are not needed to be animated.
456 | This will stop LMDD from traversing down the DOM tree and keep all children of the element 457 | intact. 458 |
467 |//Example markup: 460 | <div id="basic-example"> (scope) 461 | <div class="grid"> (container) 462 | <div class="item lmdd-block">(draggable) 463 | <--! A lot of additional markup --> 464 | </div> 465 | </div> 466 | </div>
469 |
Integration
473 |-
474 |
-
475 | bookmarkGuidelines476 |477 |495 |
LMDD is fairly easy to integrate with any other script. I would recommend treating the drag 478 | operation as any other user input, think of it as some kind of a sophisticated mouse 479 | gesture. Having this point of view in mind, the integration process should be as 480 | follows:
481 |-
482 |
- Upon rendering your markup, set an LMDD instance on the relevant part of the 483 | DOM 484 | 485 |
- Set the dataMode to 'true'. (Which will undo any changes LMDD made on the 486 | DOM when the drag operation ends) 487 | 488 |
- Add an eventListener for the 'lmddend' event 489 |
- Use an event handler to process the event details and implement the 490 | necessary changes on your app data/state 491 | 492 |
494 |
496 | -
497 | filter_1Vue Example498 |499 |519 |
See the Pen vue.js drag and 502 | drop with lmdd by Yair Levy (@supraniti) on CodePen.
504 | 505 |506 | Note that the relevant code for implementing the drag and drop operation is made out of two 507 | simple functions, 508 |
518 |-
509 |
- setting up lmdd and adding an event listener (on Vue 'mounted' 510 | function). 511 | 512 |
- moving the relevant data object from it's original parent into it's new 513 | parent (on our 'handleDragEvent' function) 514 | 515 |
520 | -
521 | code522 |523 |549 |
548 |mounted: function() { 524 | //set lmdd 525 | lmdd.set(document.getElementById('drag-scope'), { 526 | containerClass: 'todo-container', 527 | draggableItemClass: 'todo-item', 528 | handleClass: 'handle', 529 | dataMode: true //set dataMode to true 530 | }); 531 | //listen to 'lmddend' event 532 | this.$el.addEventListener('lmddend', this.handleDragEvent); 533 | } 534 | methods: { 535 | //handle 'lmddend' event 536 | handleDragEvent: function(event) { 537 | // Process the event detail 538 | var newIndex = event.detail.to.index; 539 | var oldIndex = event.detail.from.index; 540 | var newContainer = event.detail.to.container.__vue__.data; 541 | var oldContainer = event.detail.from.container.__vue__.data; 542 | if (event.detail.dragType === 'move') { 543 | //implement the changes on the app data 544 | newContainer.splice(newIndex, 0, oldContainer.splice(oldIndex, 1)[0]); 545 | } 546 | } 547 | }
550 |
Angular and React integration is pretty much the same, examples will be added to this section 552 | later on. 553 |554 |
More
557 |
558 | Bug reports & feature requests are more than welcome.
559 | Feel free to ask questions and contribute on github
561 |
If you are interested in a drag&drop functionality for your web apps, you might also like:
563 |-
564 |
- jQuery UI - sortable 565 |
- Dragula - "Drag and drop so simple it 566 | hurts" 567 |
- Sortable - no jQuery 568 |
- nestedSortable jQuery Plugin 569 | 570 |
- React DnD 571 |
- angular-drag-and-drop-lists 572 | 573 |