Sortable是一个可拖放列表重新排序的JavaScript库

Sortable


Sortable 是一个用于可重新排序的拖放列表的 JavaScript 库。

Demo: http://sortablejs.github.io/Sortable/

功能

  • 支持触控设备和现代浏览器(包括 IE9)
  • 支持从一个列表拖到另一个列表,或在同一列表内拖放
  • 移动项目时支持 CSS 动画
  • 支持拖动手柄和可选文本(优于 voidberg 的 html5sortable)
  • 智能自动滚动
  • 高级交换检测
  • 流畅的动画
  • 支持多点拖动
  • 支持 CSS 变换
  • 使用原生 HTML5 拖放 API 构建
  • 支持
  • 支持任何 CSS 库,例如Bootstrap
  • 简单的 API
  • 支持 插件
  • CDN
  • 无需 jQuery(但有 支持
  • Typescript 定义位于 @types/sortablejs

入门

使用 NPM 安装:

bash 复制代码
npm install sortablejs --save

使用 Bower 安装:

bash 复制代码
bower install --save sortablejs

导入到你的项目中:

js 复制代码
// 默认 SortableJS
import Sortable from 'sortablejs';

// 核心 SortableJS(不含默认插件)
import Sortable from 'sortablejs/modular/sortable.core.esm.js';

// 完整 SortableJS(包含所有插件)
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';

Cherrypick 插件:

js 复制代码
// Cherrypick 额外插件
import Sortable, { MultiDrag, Swap } from 'sortablejs';

Sortable.mount(new MultiDrag(), new Swap());

// Cherrypick 默认插件
import Sortable, { AutoScroll } from 'sortablejs/modular/sortable.core.esm.js';

Sortable.mount(new AutoScroll());

用法

html 复制代码
<ul id="items">
	<li>item 1</li>
	<li>item 2</li>
	<li>item 3</li>
</ul>
js 复制代码
var el = document.getElementById('items');
var sortable = Sortable.create(el);

您可以使用任何元素作为列表及其元素,而不仅仅是 ul/li。这是一个使用 div 的示例

选项

js 复制代码
var sortable = new Sortable(el, {
        group: "name", // 或 { name: "...", pull: [true, false, 'clone', array], put: [true, false, array] }
        sort: true, // 在列表内排序
        delay: 0, // 定义排序开始时间(毫秒)
        delayOnTouchOnly: false, // 仅在用户触摸时延迟
        touchStartThreshold: 0, // 像素,在取消延迟拖动事件之前,点应移动的像素数
        disabled: false, // 如果设置为 true,则禁用可排序对象。
        store: null, // @see Store
        animation: 150, // 毫秒,排序时移动项目的动画速度,`0` — 不使用动画
        easing: "cubic-bezier(1, 0, 0, 1)", // 动画的缓动。默认为 null。示例请参阅 https://easings.net/。
        handle: ".my-handle", // 列表项内的拖动手柄选择器
        filter: ".ignore-elements", // 不会导致拖动的选择器(字符串或函数)
        preventOnFilter: true, // 触发 `filter` 时调用 `event.preventDefault()`
        draggable: ".item", // 指定元素内哪些项目应可拖动
        
        dataIdAttr: 'data-id', // `toArray()` 方法使用的 HTML 属性
        
        ghostClass: "sortable-ghost", // 放置占位符的 Class 名称
        chosenClass: "sortable-chosen", // 所选项目的 Class 名称
        dragClass: "sortable-drag", // 拖动项目的 Class 名称
        
        swapThreshold: 1, // 交换区域的阈值
        invertSwap: false, // 如果设置为 true,则始终使用反向交换区域
        invertedSwapThreshold: 1, // 反向交换区域的阈值(将设置为(默认为 swapThreshold 值)
        direction: 'horizo​​ntal', // Sortable 的方向(如果未指定,将自动检测)
        
        forceFallback: false, // 忽略 HTML5 DnD 行为并强制启用 fallback
        
        fallbackClass: "sortable-fallback", // 使用 forceFallback 时克隆的 DOM 元素的 Class 名
        fallbackOnBody: false, // 将克隆的 DOM 元素附加到 Document 的 Body 中
        fallbackTolerance: 0, // 指定鼠标在被视为拖动之前应移动的距离(以像素为单位)。
        
        dragoverBubble: false,
        removeCloneOnHide: true, // 当克隆元素未显示时,将其移除,而不是隐藏它
        emptyInsertThreshold: 5, // 鼠标与空的 sortable 之间的距离,以便将拖动元素插入其中


	setData: function (/** DataTransfer */dataTransfer, /** HTMLElement*/dragEl) {
		dataTransfer.setData('Text', dragEl.textContent); // `dataTransfer` object of HTML5 DragEvent
	},

	// Element is chosen
	onChoose: function (/**Event*/evt) {
		evt.oldIndex;  // element index within parent
	},

	// Element is unchosen
	onUnchoose: function(/**Event*/evt) {
		// same properties as onEnd
	},

	// Element dragging started
	onStart: function (/**Event*/evt) {
		evt.oldIndex;  // element index within parent
	},

	// Element dragging ended
	onEnd: function (/**Event*/evt) {
		var itemEl = evt.item;  // dragged HTMLElement
		evt.to;    // target list
		evt.from;  // previous list
		evt.oldIndex;  // element's old index within old parent
		evt.newIndex;  // element's new index within new parent
		evt.oldDraggableIndex; // element's old index within old parent, only counting draggable elements
		evt.newDraggableIndex; // element's new index within new parent, only counting draggable elements
		evt.clone // the clone element
		evt.pullMode;  // when item is in another sortable: `"clone"` if cloning, `true` if moving
	},

	// Element is dropped into the list from another list
	onAdd: function (/**Event*/evt) {
		// same properties as onEnd
	},

	// Changed sorting within list
	onUpdate: function (/**Event*/evt) {
		// same properties as onEnd
	},

	// Called by any change to the list (add / update / remove)
	onSort: function (/**Event*/evt) {
		// same properties as onEnd
	},

	// Element is removed from the list into another list
	onRemove: function (/**Event*/evt) {
		// same properties as onEnd
	},

	// Attempt to drag a filtered element
	onFilter: function (/**Event*/evt) {
		var itemEl = evt.item;  // HTMLElement receiving the `mousedown|tapstart` event.
	},

	// Event when you move an item in the list or between lists
	onMove: function (/**Event*/evt, /**Event*/originalEvent) {
		// Example: https://jsbin.com/nawahef/edit?js,output
		evt.dragged; // dragged HTMLElement
		evt.draggedRect; // DOMRect {left, top, right, bottom}
		evt.related; // HTMLElement on which have guided
		evt.relatedRect; // DOMRect
		evt.willInsertAfter; // Boolean that is true if Sortable will insert drag element after target by default
		originalEvent.clientY; // mouse position
		// return false; — for cancel
		// return -1; — insert before target
		// return 1; — insert after target
		// return true; — keep default insertion point based on the direction
		// return void; — keep default insertion point based on the direction
	},

	// Called when creating a clone of element
	onClone: function (/**Event*/evt) {
		var origEl = evt.item;
		var cloneEl = evt.clone;
	},

	// Called when dragging element changes position
	onChange: function(/**Event*/evt) {
		evt.newIndex // most likely why this event is used is to get the dragging element's current index
		// same properties as onEnd
	}
});

group 选项

要将元素从一个列表拖到另一个列表,两个列表必须具有相同的 group 值。
您还可以定义列表是否可以放弃、提供并保留副本(“clone”)以及接收元素。

  • name:String — 组名称
  • pull:true|false|["foo", "bar"]|'clone'|function — 是否可以从列表中移动。clone — 复制项目,而不是移动。或者是一个可以放入元素的组名称数组。默认为 true
  • put:true|false|["baz", "qux"]|function — 是否可以从其他列表添加元素,或者是一个可以从中添加元素的组名称数组。
  • revertClone:boolean — 移动到另一个列表后,将克隆的元素还原到初始位置。

示例:


sort 选项

允许在列表内排序。

demo:https://jsbin.com/jayedig/edit?js,output

delay 选项

以毫秒为单位,用于定义排序的开始时间。
遗憾的是,由于浏览器限制,在 IE 或 Edge 上无法使用原生拖放功能进行延迟。

演示:https://jsbin.com/zosiwah/edit?js,output


delayOnTouchOnly 选项

是否仅在用户使用触摸操作时(例如在移动设备上)应用延迟。其他情况下均不应用延迟。默认为 false


swapThreshold 选项

交换区域占目标的百分比,以介于 01 之间的浮点数表示。

阅读更多

演示:http://sortablejs.github.io/Sortable#thresholds


invertSwap 选项

设置为 true 可将交换区域设置为目标的两侧,以实现对“中间”项目进行排序的效果。

阅读更多

演示:http://sortablejs.github.io/Sortable#thresholds


invertedSwapThreshold 选项

反转交换区域占目标的百分比,以介于 01 之间的浮点数表示。如果未指定,则默认为 swapThreshold

阅读更多


direction 选项

可排序元素的排序方向。可以设置为 'vertical''horizo​​ntal' 或一个函数,该函数会在目标被拖拽时被调用。必须返回 'vertical''horizo​​ntal'

阅读更多

包含整列和半列元素的垂直列表方向检测示例:

js 复制代码
Sortable.create(el, {
direction: function(evt, target, dragEl) {
if (target !== null && target.className.includes('half-column') && dragEl.className.includes('half-column')) {
return 'horizo​​ntal';
}
return 'vertical';
}
});

touchStartThreshold 选项

此选项与 fallbackTolerance 选项类似。

设置 delay 选项后,某些配备高灵敏度触摸屏的手机(例如三星 Galaxy S8)即使手指未移动,也会触发
不必要的 touchmove 事件,导致排序无法触发。

此选项设置取消延迟排序之前必须发生的最小指针移动次数。

值最好在 3 到 5 之间。


disabled 选项

如果设置为 true,则禁用可排序功能。

演示:https://jsbin.com/sewokud/edit?js,output

js 复制代码
var sortable = Sortable.create(list);

document.getElementById("switcher").onclick = function () {
  var state = sortable.option("disabled"); // 获取
  
  sortable.option("disabled", !state); // 设置
};

handle 选项

为了使列表项可拖动,Sortable 会禁用用户的文本选择。
这并不总是可取的。为了允许文本选择,请定义一个拖动处理程序,
它是每个列表元素中允许拖动的区域。

演示:https://jsbin.com/numakuh/edit?html,js,output

js 复制代码
Sortable.create(el, {
handle: ".my-handle"
});
html 复制代码
<ul>
<li><span class="my-handle">::</span> 列表项文本一
<li><span class="my-handle">::</span> 列表项文本二
</ul>
css 复制代码
.my-handle {
cursor: move;
cursor: -webkit-grabbing;
}

filter 选项

js 复制代码
Sortable.create(list, {
	filter: ".js-remove, .js-edit",
	onFilter: function (evt) {
		var item = evt.item,
			ctrl = evt.target;

		if (Sortable.utils.is(ctrl, ".js-remove")) {  // Click on remove button
			item.parentNode.removeChild(item); // remove sortable item
		}
		else if (Sortable.utils.is(ctrl, ".js-edit")) {  // Click on edit link
			// ...
		}
	}
})

ghostClass 选项

放置占位符的类名(默认为“sortable-ghost”)。

Demo: https://jsbin.com/henuyiw/edit?css,js,output

css 复制代码
.ghost {
  opacity: 0.4;
}
js 复制代码
Sortable.create(list, {
  ghostClass: "ghost"
});

chosenClass 选项

所选项目的类名(默认为“sortable-chosen”)。

Demo: https://jsbin.com/hoqufox/edit?css,js,output

css 复制代码
.chosen {
  color: #fff;
  background-color: #c00;
}
js 复制代码
Sortable.create(list, {
  delay: 500,
  chosenClass: "chosen"
});

forceFallback 选项

如果设置为 true,即使我们使用的是 HTML5 浏览器,也会使用非 HTML5 浏览器的回退机制。
这使我们能够在较新的浏览器中测试旧版浏览器的行为,或者使拖放操作在桌面、移动和旧版浏览器之间感觉更加一致。

此外,回退机制始终会生成该 DOM 元素的副本,并附加在选项中定义的 fallbackClass 类。此行为控制此“拖动”元素的外观。

演示:https://jsbin.com/sibiput/edit?html,css,js,output


fallbackTolerance 选项

模拟原生拖动阈值。以像素为单位指定鼠标移动多远才被视为拖动。
如果项目也可以点击,例如在链接列表中,则非常有用。

当用户在可排序元素内部点击时,从按下到释放之间手部略微移动是很常见的。
只有当指针移动超过一定容差时才会开始拖动,这样就不会每次点击时都意外开始拖动。

3 到 5 可能是比较合适的值。


dragoverBubble 选项

如果设置为 true,dragover 事件将冒泡到父级可排序元素。适用于回退事件和原生 dragover 事件。
默认情况下,该选项为 false,但只有当元素已插入父级可排序元素,或者可以插入父级可排序元素,但尚未插入(由于动画等原因)时,Sortable 才会停止冒泡事件。

从 1.8.0 开始,您可能希望将此选项保留为 false。在 1.8.0 之前,嵌套可排序元素可能需要设置为 true 才能正常工作。


removeCloneOnHide 选项

如果设置为 false,则克隆元素会通过将其 CSS display 属性设置为 none 来隐藏。
默认情况下,此选项为 true,这意味着当克隆元素应该隐藏时,Sortable 会将其从 DOM 中移除。


emptyInsertThreshold 选项

拖动时,鼠标与空的可排序元素之间的距离(以像素为单位),拖动元素才会插入到该可排序元素中。默认为 5。设置为 0 可禁用此功能。

演示:https://jsbin.com/becavoj/edit?js,output

此选项的另一种方法是在列表为空时设置内边距。

例如:

css 复制代码
ul:empty {
padding-bottom: 20px;
}

警告:要使 :empty 生效,它内部必须没有任何节点(即使是文本节点)。

演示:https://jsbin.com/yunakeg/edit?html,css,js,output


事件对象 (demo)

  • to:HTMLElement — 移动元素所在的列表
  • from:HTMLElement — 上一个列表
  • item:HTMLElement — 被拖拽元素
  • clone:HTMLElement
  • oldIndex:Number|undefined — 父元素中的旧索引
  • newIndex:Number|undefined — 父元素中的新索引
  • oldDraggableIndex:Number|undefined — 父元素中的旧索引,仅计算可拖拽元素
  • newDraggableIndex:Number|undefined — 父元素中的新索引,仅计算可拖拽元素
  • pullMode:String|Boolean|undefined — 拖拽至另一个可排序元素时为拖拽模式 ("clone"truefalse),否则未定义

move 事件对象

  • to:HTMLElement
  • from:HTMLElement
  • dragged:HTMLElement
  • draggedRect:DOMRect
  • related:HTMLElement — 已引导的元素
  • relatedRect:DOMRect
  • willInsertAfter:Boolean — 元素插入目标后,返回 true(插入目标前,返回 false

方法

option(name:String[, value:*]):*

获取或设置选项。

closest(el:HTMLElement[, selector:String]):HTMLElement|null

对于集合中的每个元素,通过测试元素本身并遍历其在 DOM 树中的祖先元素,获取第一个与选择器匹配的元素。

toArray():String[]

将可排序项的 data-iddataIdAttr 选项)序列化为字符串数组。

sort(order:String[], useAnimation:Boolean)

根据数组对元素进行排序。

js 复制代码
var order = sortable.toArray();
sortable.sort(order.reverse(), true); // 应用
save()

保存当前排序(参见 store

destroy()

彻底移除可排序功能。


Store

Saving and restoring of the sort.

html 复制代码
<ul>
	<li data-id="1">order</li>
	<li data-id="2">save</li>
	<li data-id="3">restore</li>
</ul>
js 复制代码
Sortable.create(el, {
	group: "localStorage-example",
	store: {
		/**
		 * Get the order of elements. Called once during initialization.
		 * @param   {Sortable}  sortable
		 * @returns {Array}
		 */
		get: function (sortable) {
			var order = localStorage.getItem(sortable.options.group.name);
			return order ? order.split('|') : [];
		},

		/**
		 * Save the order of elements. Called onEnd (when the item is dropped).
		 * @param {Sortable}  sortable
		 */
		set: function (sortable) {
			var order = sortable.toArray();
			localStorage.setItem(sortable.options.group.name, order.join('|'));
		}
	}
})

Bootstrap

Demo: https://jsbin.com/visimub/edit?html,js,output

html 复制代码
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>


<!-- Latest Sortable -->
<script src="http://SortableJS.github.io/Sortable/Sortable.js"></script>


<!-- Simple List -->
<ul id="simpleList" class="list-group">
	<li class="list-group-item">This is <a href="http://SortableJS.github.io/Sortable/">Sortable</a></li>
	<li class="list-group-item">It works with Bootstrap...</li>
	<li class="list-group-item">...out of the box.</li>
	<li class="list-group-item">It has support for touch devices.</li>
	<li class="list-group-item">Just drag some elements around.</li>
</ul>

<script>
    // Simple list
    Sortable.create(simpleList, { /* options */ });
</script>

静态方法和属性

Sortable.create(el:HTMLElement[, options:Object]):Sortable

创建新实例。


Sortable.active:Sortable

活动的 Sortable 实例。


Sortable.dragged:HTMLElement

被拖动的元素。


Sortable.ghost:HTMLElement

幽灵元素。


Sortable.clone:HTMLElement

克隆元素。


Sortable.get(element:HTMLElement):Sortable

获取元素上的 Sortable 实例。


Sortable.mount(plugin:...SortablePlugin|SortablePlugin[])

将插件挂载到 Sortable。


Sortable.utils
  • on(el:HTMLElement, event:String, fn:Function) — 附加事件处理函数
  • off(el:HTMLElement, event:String, fn:Function) — 移除事件处理函数
  • css(el:HTMLElement):Object — 获取所有 CSS 属性的值
  • css(el:HTMLElement, prop:String):Mixed — 获取样式属性的值
  • css(el:HTMLElement, prop:String, value:String) — 设置一个 CSS 属性
  • css(el:HTMLElement, props:Object) — 设置更多 CSS 属性
  • find(ctx:HTMLElement, tagName:String[, iterator:Function]):Array — 通过标签名称获取元素
  • bind(ctx:Mixed, fn:Function):Function — 接受一个函数并返回一个始终具有特定上下文的新函数
  • is(el:HTMLElement, selector:String):Boolean — 根据选择器检查当前匹配的元素集合
  • closest(el:HTMLElement, selector:String[, ctx:HTMLElement]):HTMLElement|Null — 对于集合中的每个元素,通过测试元素本身并遍历其在 DOM 树中的祖先,获取第一个与选择器匹配的元素
  • clone(el:HTMLElement):HTMLElement — 创建匹配元素集合的深层副本
  • toggleClass(el:HTMLElement, name:String, state:Boolean) — 为每个元素添加或删除一个类
  • detectDirection(el:HTMLElement):String — 自动检测元素的 direction 方向,可以是 'vertical''horizo​​ntal'
  • index(el:HTMLElement, selector:String):Number — 元素在其父级中选定元素集合的索引
  • getChild(el:HTMLElement, childNum:Number, options:Object, includeDragEl:Boolean):HTMLElement — 获取 Sortable 实例中指定索引处的可拖动元素
  • expando:String — 内部使用的 expando 属性名称,sortableListElement[expando] 返回该元素的 Sortable 实例

插件

额外插件(包含在完整版本中)

默认插件(默认版本已包含)


CDN

html 复制代码
<!-- jsDelivr :: Sortable :: Latest (https://www.jsdelivr.com/package/npm/sortablejs) -->
<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>

MIT 许可证

特此授予获得本软件及相关文档文件(以下简称“软件”)副本的任何人免费许可,以无限制方式处理本软件,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售本软件副本的权利,以及允许获得本软件的人员进行上述操作的权利,但须遵守以下条件:

上述版权声明和本许可声明应包含在软件的所有副本或重要部分中。

本软件“按原样”提供,不作任何形式的保证,无论明示或暗示,包括但不限于适销性、特定用途适用性和不侵权性的保证。在任何情况下,作者或版权持有人均不承担任何索赔、损害或其他责任,无论是合同诉讼、侵权诉讼还是其他诉讼,无论其是否因本软件、本软件的使用或其他交易而引起、产生或与之相关。

关于项目

Sortable 是一个用于可重新排序的拖放列表的 JavaScript 库,它不依赖于jQuery 或其它框架。
MIT
Javascript
30,792
3742
403
2013-12-19
2025-05-30

增长趋势 - stars