diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cab04be --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio + +## Directory-based project format: +.idea/ \ No newline at end of file diff --git a/README.md b/README.md index a16b7fd..9fcbfac 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,43 @@ #jquery-dragarrange -### A very basic jQuery plugin to shift element position by drag +### A jQuery plugin to shift element position by drag & drop -If you are looking for a library which can be ordered/arranged by drag, you are at right place. This plugin doesn't require any CSS change, just call the function on elements you want to be arranged, and you are done. +If you are looking for a library which can be ordered/arranged by drag, you are at right place. +This plugin doesn't require any CSS change, just call the function on elements you want to be arranged, and you are done. +The intent of the plugin is to: + +* Allow easy drag and drop reordering of elements within a container +* Allow for elements added dynamically to also be sortable +* Automatically scroll when reaching window border for long lists How to Use ---------- ```html
-
Drag 1
-
Drag 2
-
Drag 3
-
Drag 4
+
Drag 1
+
Drag 2
+
Drag 3
+
Drag 4
``` Call the function: -$('.draggable-element').arrangeable(); +```javascript +$('.draggable-element').dragArrange('.draggable-element'); +``` +Note that the selector is provided twice: once as a jQuery selector, and once as a dragArrange argument. +This may seem redundant. However, the selector is used internally and since [jQuery has deprecated the .selector property](http://api.jquery.com/selector/), +this was a low friction way of accessing the data reliably. Optional Parameters ------------------- +###cssPrefix +String to be prepended to the default "dragging" class assigned to any actively dragging DOM element. + +###containerSelector +jQuery selector to define the single parent over all draggable element containers that should behave together. + ###dragSelector -If passed, object can be dragged only from this selector. The default dragSelector is same DOM element that has called the function. +jQuery selector of element within a draggable element to be used as the drag handle. If not set, the entire draggable +element acts as the handle. ###Setup ``` @@ -28,8 +46,8 @@ bower install jquery-dragarrange Or you can download latest library from [here](https://github.com/vishalok12/jquery-dragarrange/releases). ###Demo -http://vishalok12.github.io/jquery-dragarrange/ +http://jaredcarlow.com/projects/jquery-dragarrange/ ### License Dragarrange is licensed under the [MIT license](http://opensource.org/licenses/MIT). -Copyright (c) 2014 [Vishal Kumar](http://github.com/vishalok12) +Copyright (c) 2014 [Vishal Kumar](http://github.com/vishalok12), 2015 Jared Carlow diff --git a/drag-arrange.js b/drag-arrange.js index aa7a189..974c696 100644 --- a/drag-arrange.js +++ b/drag-arrange.js @@ -1,273 +1,340 @@ /** - * drag-shift + * drag-arrange * http://github.com/vishalok12 - * Copyright (c) 2014 Vishal Kumar + * Copyright (c) 2014 Vishal Kumar; 2015 Jared Carlow * Licensed under the MIT License. */ 'use strict'; (function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else { - // Browser globals - factory(jQuery); - } -}(function ($) { - var IS_TOUCH_DEVICE = ('ontouchstart' in document.documentElement); - /** - * mouse move threshold (in px) until drag action starts - * @type {Number} - */ - var DRAG_THRESHOLD = 5; - /** - * to generate event namespace - * @type {Number} - */ - var counter = 0; - - /** - * Javascript events for touch device/desktop - * @return {Object} - */ - var dragEvents = (function () { - if (IS_TOUCH_DEVICE) { - return { - START: 'touchstart', - MOVE: 'touchmove', - END: 'touchend' - }; - } - else { - return { - START: 'mousedown', - MOVE: 'mousemove', - END: 'mouseup' - }; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(['jquery'], factory); + } else { + // Browser globals + factory(jQuery); } - }()); - - $.fn.arrangeable = function(options) { - var dragging = false; - var $clone; - var dragElement; - var originalClientX, originalClientY; // client(X|Y) position before drag starts - var $elements; // list of elements to shift between - var touchDown = false; - var leftOffset, topOffset; - var eventNamespace; - - if (typeof options === "string") { - // check if want to destroy drag-arrange - if (options === 'destroy') { - if (this.eq(0).data('drag-arrange-destroy')) { - this.eq(0).data('drag-arrange-destroy')(); +}(function ($) { + /** + * mouse move threshold (in px) until drag action starts + * @type {Number} + */ + var DRAG_THRESHOLD = 5; + + /** + * Javascript events to start dragging, move, and stop dragging. If feature + * detection is needed, this would be the place to do it, returning an object + * with these three properties. + * @return {Object} + */ + var dragEvents = { + START : 'mousedown touchstart', + MOVE : 'mousemove touchmove', + END : 'mouseup touchend' + }; + + $.fn.dragArrange = function (dragElementsSelector, optionsOverrides) { + if (!dragElementsSelector) { + console.log('Cannot initialize arrangeable without drag elements selector specified!'); + return; } - return this; - } - } + // Options defaults + var options = { + cssPrefix : '', // Prefix used for classes added + containerSelector : '', // Selector for container when not all draggable items share immediate parent as container + scrollSpeed : 15 // Number of pixels to move at a time at full speed + }; + + // Override options if provided + options = $.extend(options, optionsOverrides); + + var dragging = false; // Current state + var $clone; // jQuery object being visually dragged around + var dragElement; // DOM element being moved (not the cloned version) + var $dragElement; // jQuery object of the real element being dragged around + var originalClientX, originalClientY; // Client(X|Y) position before drag starts + var currentX, currentY; // The updated (X|Y) position of pointer + var touchDown = false; // Flag to note touch start event before movement threshold is reached + var leftOffset, topOffset; // CSS left and top property distance of dragging element + var $elements = this; // List of jQuery object elements to shift between - reload each time to catch dynamic elements + + /* The DOM element that contains all sortable elements ($elements) for group. + * Will default to parent of first element, but can be overridden by providing + * a selector in the function call options object: + * $('.draggable-items').arrangeable('.draggable-items',{containerSelector: '.container'}); + * This is especially important when not all draggable items are in the + * same immediate parent container. Knowing the common parent allows the + * auto scroll functionality to work! + */ + var $container = options.containerSelector ? $(options.containerSelector) : $($elements[0]).parent(); + + // Attach handler at container level to allow for dynamic element addition! + var dragElementDragSelector = (options.dragSelector ? options.dragSelector : separateElementsSelectorFromContainer()); + + $container.on(dragEvents.START, dragElementDragSelector, dragStartHandler); + + // Bind mousemove/touchmove to document (as it is not compulsory + // that event will trigger on the dragging element) + $(document).add(window) + .on(dragEvents.MOVE, dragMoveHandler) + .on(dragEvents.END, dragEndHandler); + + /* In order to detect dynamic elements, we need to bind events to their container so when + * they bubble up they can be noticed. In order to detect the events properly, we need to filter the events. + * This is done by applying a selector filter. The filter selector CANNOT contain pieces of the + * container selector or it won't recognize it. So for example, if we have: * + * $('ul.container li.dragElement').dragArrange('ul.container li.dragElement'); + * The plugin will bind events to the ('ul.container') element and need to filter by ('li.dragElement'). + * If it tries to filter by ('ul.container li.dragElement') it will fail, because of course the