ddsort.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. ;(function ($) {
  2. /**
  3. * Author: https://github.com/Barrior
  4. *
  5. * DDSort: drag and drop sorting.
  6. * @param {Object} options
  7. * target[string]: 可选,jQuery事件委托选择器字符串,默认'li'
  8. * cloneStyle[object]: 可选,设置占位符元素的样式
  9. * floatStyle[object]: 可选,设置拖动元素的样式
  10. * down[function]: 可选,鼠标按下时执行的函数
  11. * move[function]: 可选,鼠标移动时执行的函数
  12. * up[function]: 可选,鼠标抬起时执行的函数
  13. * draggableArea[string]:可选,设置可拖拽的区域
  14. */
  15. $.fn.DDSort = function (options) {
  16. var $doc = $(document),
  17. fnEmpty = function () {
  18. },
  19. settings = $.extend(true, {
  20. down: fnEmpty,
  21. move: fnEmpty,
  22. up: fnEmpty,
  23. target: 'li',
  24. cloneStyle: {
  25. 'background-color': '#f7f8fa'
  26. },
  27. floatStyle: {
  28. //用固定定位可以防止定位父级不是Body的情况的兼容处理,表示不兼容IE6,无妨
  29. 'position': 'fixed',
  30. 'box-shadow': '10px 10px 20px 0 #eee',
  31. /*'webkitTransform': 'rotate(4deg)',
  32. 'mozTransform': 'rotate(4deg)',
  33. 'msTransform': 'rotate(4deg)',
  34. 'transform': 'rotate(4deg)'*/
  35. },
  36. draggableArea: ''
  37. }, options);
  38. return this.each(function () {
  39. var that = $(this),
  40. height = 'height',
  41. width = 'width';
  42. if (that.css('box-sizing') == 'border-box') {
  43. height = 'outerHeight';
  44. width = 'outerWidth';
  45. }
  46. that.on('mousedown.DDSort', settings.target, function (e) {
  47. //只允许鼠标左键拖动
  48. if (e.which != 1) {
  49. return;
  50. }
  51. //防止表单元素失效
  52. var tagName = e.target.tagName.toLowerCase();
  53. if (tagName == 'input' || tagName == 'textarea' || tagName == 'select') {
  54. return;
  55. }
  56. var THIS = this,
  57. $this = $(THIS),
  58. offset = $this.offset(),
  59. disX = e.pageX - offset.left,
  60. disY = e.pageY - offset.top,
  61. clone = $this.clone()
  62. .css(settings.cloneStyle)
  63. .css('height', $this[height]())
  64. .empty(),
  65. hasClone = 1,
  66. //缓存计算
  67. thisOuterHeight = $this.outerHeight(),
  68. thatOuterHeight = that.outerHeight(),
  69. //滚动速度
  70. upSpeed = thisOuterHeight,
  71. downSpeed = thisOuterHeight,
  72. maxSpeed = thisOuterHeight * 3;
  73. if (settings.draggableArea != "") {
  74. //判断当前点击的DOM是否允许拖拽
  75. var isDraggable = recursiveQuery($(e.target), settings.draggableArea);
  76. // 特殊处理:带有该属性的禁用
  77. if($(e.target).parent().attr("data-disabled") || $(e.target).attr("data-disabled")){
  78. return;
  79. }
  80. if (!isDraggable) {
  81. return;
  82. }
  83. }
  84. settings.down.call(THIS);
  85. $doc.on('mousemove.DDSort', function (e) {
  86. if (hasClone) {
  87. $this.before(clone)
  88. .css('width', $this[width]())
  89. .css(settings.floatStyle)
  90. .appendTo($this.parent());
  91. hasClone = 0;
  92. }
  93. var left = e.pageX - disX,
  94. top = e.pageY - disY,
  95. prev = clone.prev(),
  96. next = clone.next().not($this);
  97. var gap = $(window).scrollTop();
  98. var calculate = top - gap;
  99. //检测是否滚动了
  100. top = ((top - $(window).scrollTop()) != top) ? calculate : top;
  101. $this.css({
  102. left: left,
  103. top: top,
  104. zIndex: 999
  105. });
  106. //向上排序
  107. if (prev.length && top < (prev.offset().top - gap) + prev.outerHeight() / 2) {
  108. clone.after(prev);
  109. //向下排序
  110. } else if (next.length && top + thisOuterHeight > (next.offset().top - gap) + next.outerHeight() / 2) {
  111. clone.before(next);
  112. }
  113. /**
  114. * 处理滚动条
  115. * that是带着滚动条的元素,这里默认以为that元素是这样的元素(正常情况就是这样),如果使用者事件委托的元素不是这样的元素,那么需要提供接口出来
  116. */
  117. var thatScrollTop = that.scrollTop(),
  118. thatOffsetTop = that.offset().top,
  119. scrollVal;
  120. //向上滚动
  121. if (top < thatOffsetTop) {
  122. downSpeed = thisOuterHeight;
  123. upSpeed = ++upSpeed > maxSpeed ? maxSpeed : upSpeed;
  124. scrollVal = thatScrollTop - upSpeed;
  125. //向下滚动
  126. } else if (top + thisOuterHeight - thatOffsetTop > thatOuterHeight) {
  127. upSpeed = thisOuterHeight;
  128. downSpeed = ++downSpeed > maxSpeed ? maxSpeed : downSpeed;
  129. scrollVal = thatScrollTop + downSpeed;
  130. }
  131. that.scrollTop(scrollVal);
  132. var index = recursiveQueryIndex($(THIS));
  133. settings.move.call(THIS, index);
  134. })
  135. .on('mouseup.DDSort', function () {
  136. $doc.off('mousemove.DDSort mouseup.DDSort');
  137. //click的时候也会触发mouseup事件,加上判断阻止这种情况
  138. if (!hasClone) {
  139. clone.before($this.removeAttr('style')).remove();
  140. var index = recursiveQueryIndex($(THIS));
  141. settings.up.call(THIS, index);
  142. }
  143. });
  144. return false;
  145. });
  146. });
  147. };
  148. //当前递归次数
  149. var currentRecursiveCount = 0;
  150. //最大递归次数
  151. var recursiveMaxCount = 20;
  152. /**
  153. * 递归查询当前区域是否允许拖拽
  154. * 创建时间:2018年7月3日18:18:01
  155. */
  156. function recursiveQuery(o, draggableArea) {
  157. if (o.hasClass(draggableArea)) {
  158. //允许拖拽,清空递归次数
  159. currentRecursiveCount = 0;
  160. // console.log($(o));
  161. return true;
  162. } else {
  163. if (currentRecursiveCount <= recursiveMaxCount) {
  164. currentRecursiveCount++;
  165. return recursiveQuery(o.parent(), draggableArea);
  166. } else {
  167. //清空递归次数
  168. // console.log("清空递归次数");
  169. currentRecursiveCount = 0;
  170. return false;
  171. }
  172. }
  173. }
  174. /**
  175. * 递归查询当前拖拽的下标
  176. * 创建时间:2018年7月3日18:18:01
  177. */
  178. function recursiveQueryIndex(o) {
  179. if (o.hasClass("draggable-element")) {
  180. //允许拖拽,清空递归次数
  181. currentRecursiveCount = 0;
  182. return $(o).attr("data-index");
  183. } else {
  184. if (currentRecursiveCount <= recursiveMaxCount) {
  185. currentRecursiveCount++;
  186. return recursiveQueryIndex(o.parent());
  187. } else {
  188. //清空递归次数
  189. // console.log("清空递归次数");
  190. currentRecursiveCount = 0;
  191. return -1;
  192. }
  193. }
  194. }
  195. })(jQuery);