简单的拖拽交换

实现列表拖拽排序,最简单的方法就是将想要拖拽的元素设置为dragable=“true”,然后利用drag事件处理元素位置变换。

拖拽的时候主要有以下几个事件

  • ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
  • ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
  • ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
  • ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
  • ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上

实现一个最简单的拖拽交换顺序,可以通过以下步骤来实现

  1. ondragstart 记录初始拖拽元素,需要注意的是在Firefox浏览器中必须调用 dataTransfer.setData() 设置拖拽的值否则无法拖拽
  2. ondragover e.preventDefault() 阻止ondragover可能导致ondrop无法触发
  3. ondrop 记录目标元素,并交换初始元素与目标元素

Sortable用法

实现更顺滑的拖拽排序可以通过Sortable 来实现。
Sortable采用的是MIT协议,不依赖任何其它的库,使用原生html5 DnD来实现,并且只有1500行。
查看Demo

用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
Sortable.create(document.getElementById('foo'), {
group: "name", // 列表名,在多列表中互相交换时为Object,形如{name:"...",pull:true,put:false,revertClone:false}多列表需要设置相同的name,pull和put表示拖拽入和出的各种情况
sort: true, // 是否在列表中排序
delay: 0, // 毫秒值,定义何时排序开始
touchStartThreshold: 0, // px, how many pixels the point should move before cancelling a delayed drag event
disabled: false, // 设定为true则禁止排序
store: null, // @see Store
animation: 150, // 毫秒值,移动的动画时间,设置为0则没有动画效果
// 以下都是一些元素选择器命名
handle: ".my-handle", // Drag handle selector within list items
filter: ".ignore-elements", // Selectors that do not lead to dragging (String or Function)
preventOnFilter: true, // Call `event.preventDefault()` when triggered `filter`
draggable: ".item", // Specifies which items inside the element should be draggable
ghostClass: "sortable-ghost", // Class name for the drop placeholder
chosenClass: "sortable-chosen", // Class name for the chosen item
dragClass: "sortable-drag", // Class name for the dragging item
dataIdAttr: 'data-id',

forceFallback: false, // 是否忽略HTML5 原生dnd行为

fallbackClass: "sortable-fallback", // Class name for the cloned DOM Element when using forceFallback
fallbackOnBody: false, // Appends the cloned DOM Element into the Document's Body
fallbackTolerance: 0, // Specify in pixels how far the mouse should move before it's considered as a drag.

scroll: true, // or HTMLElement
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
scrollSpeed: 10, // px

setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
},
onChoose: function () { // 元素被选择时触发该事件
console.log('onChoose.foo:', [evt.item]);
},
onStart:function(evt){ //开始拖拽触发该事件
console.log('onStart.foo:', [evt.item]);
},
onEnd: function(evt){ //拖拽完毕之后触发该事件
console.log('onEnd.foo:', [evt.item, evt.from, evt.to, evt.oldIndex, evt.newIndex]);
}
onAdd: function (evt){ //从另外一个列表拖找到当前列表时触发该事件
console.log('onAdd.foo:', [evt.item]);
},
onUpdate: function (evt){ //在列表内更新位置时触发该事件
console.log('onUpdate.foo:', [evt.item]);
},
onSort:function(evt){ //发生排序时触发该事件
console.log('onSort.foo:', [evt.item]);
}
onRemove: function (evt){ //从当前列表删除时触发该事件(被拖拽到另一个列表)
console.log('onRemove.foo:', [evt.item]);
},
onFilter: function (evt) {
console.log('onFilter.foo:', [evt.item]);
},
onMove: function (evt, originalEvent) {
console.log('onMove.foo:', [evt.dragged,evt.draggedRect, evt.related, evt.relatedRect, originalEvent.clientY]);
},
onClone: function (evt) {
console.log('onClone.foo:', [evt.item, evt.clone]);
}
});

Sortable在React中的用法示例

React中的示例:

react
1
2
3
4
5
6
7
8
9
Sortable.create(document.getElementsByClassName('ul-container')[0], {
animation: 150,
onSort: evt => {
let newOrderItem = this.state.orderItem;
let dragedItem = newOrderItem.splice(evt.oldIndex, 1)[0];
newOrderItem.splice(evt.newIndex, 0, dragedItem);
this.setState({ orderItem: newOrderItem });
}
});

通常,还需要将拖拽的item的样式设置为不可见

1
2
3
4
5
6
ul li {
cursor: move;
}
.sortable-ghost {
opacity: 0;
}

其它版本的Sortable

Sortable由于只采用了原生的HTML5来实现拖拽,所以可以被轻松地移植到几乎所有的框架中,例如Vue版本的SortableAngular2版本的Sortable.