Sortablejs 简介
Sortable —是一个JavaScript库,用于在现代浏览器和触摸设备上对拖放列表进行重新排序。无需jQuery。 支持Meteor,AngularJS,React,Polymer,Vue,Ember,Knockout和任何CSS库,例如Bootstrap
一看这解释感觉就是很棒的感觉
特征
- 支持触摸设备和现代浏览器(包括IE9)
- 可以从一个列表拖动到另一个列表或在同一列表内
- 支持拖动手柄和可选文本(比voidberg的html5sortable更好)
- 智能自动滚动
- 高级交换检测
- 流畅的动画
- 多拖动支持
- 支持CSS转换
- 使用原生HTML5拖放API构建
支持
- Meteor
- Angular2.0+1.*
- React
- ES2015+Mixin
- Knockout
- Polymer
- Vue
- Ember
- 支持任何CSS库,例如Bootstrap
- 简单的API
- 支持插件
- CDN
- 不需要jQuery(但有支持)
- typscript定义在 @types/sortablejs
文章
- Dragging Multiple Items in Sortable (April 26, 2019)
- Swap Thresholds and Direction (December 2, 2018)
- Sortable v1.0 — New capabilities (December 22, 2014)
- Sorting with the help of HTML5 Drag'n'Drop API (December 23, 2013)
安装
npm install sortablejs --save
导入
// Default SortableJS
import Sortable from 'sortablejs';
// Core SortableJS (without default plugins)
import Sortable from 'sortablejs/modular/sortable.core.esm.js';
// Complete SortableJS (with all plugins)
import Sortable from 'sortablejs/modular/sortable.complete.esm.js';
插件导入
// Cherrypick extra plugins
import Sortable, { MultiDrag, Swap } from 'sortablejs';
Sortable.mount(new MultiDrag(), new Swap());
// Cherrypick default plugins
import Sortable, { AutoScroll } from 'sortablejs/modular/sortable.core.esm.js';
Sortable.mount(new AutoScroll());
基本模板以及样式代码
<template>
<div @click="reportClick" class="report-wrap">
<!-- v-if="ufd.isInit" v-loading="!ufd.isInit" -->
<div class="report-container">报表</div>
<div class="select-container">
<div class="key-list-content">
<h3 class="key-list-title">字段列表</h3>
<div class="key-list">
<p class="key-list-tip">将字段拖动到数据透视区域</p>
<ul :style="styleUl" @mouseenter="ulEnter" @mouseleave="ulLeave" class="key" id="key">
<!-- <li
:data-id="li['tableColumnCtlId']"
:data-label="li['columnName']"
:data-value="li['dbFieldName']"
:key="index"
v-for="(li,index) in ufd.columns.order"
>{{li['columnName']}}</li>-->
<li
:data-id="li['id']"
:data-label="li['labelKey']"
:data-value="li['valueKey']"
:key="index"
data-fun-name="sum"
v-for="(li,index) in options"
>{{li['labelKey']}}</li>
</ul>
</div>
</div>
<div class="data-view-content">
<h3 class="key-list-title">数据透视区域</h3>
<div class="data-view">
<p class="data-view-tip">在下面区域拖动字段</p>
<div class="view">
<div class="select-view-wrap same-wrap">
<p class="select-view-title">过滤器</p>
<div class="select-view same">
<ul @click.stop="select" class="select" data-id="filter"></ul>
</div>
</div>
<div class="row-view-wrap same-wrap">
<p class="row-view-title">行</p>
<div class="row-view other">
<ul @click.stop="row" class="row" data-id="row"></ul>
</div>
</div>
<div class="what-view-wrap same-wrap">
<p class="what-view-title">值</p>
<div class="what-view same">
<ul @click.stop="what" class="what" data-id="value"></ul>
</div>
</div>
</div>
</div>
</div>
<!-- 更新按钮 start -->
<div class="update">
<button @click="update">更新</button>
</div>
<!-- 更新按钮 end -->
</div>
<!-- 右键 出现 menu 菜单 start -->
<div id="menu" v-show="menuShow">
<div
:class="'menu'"
:key="index"
@click.stop="menuClick($event, index, item)"
v-for="(item, index) in menus"
>{{item.value}}</div>
</div>
<!-- 右键 出现 menu 菜单 end -->
<!-- 字段设置 start -->
<div @click.stop class="setting" v-if="settingShow">
<p class="title">字段设置</p>
<div class="setting-content">
<p class="origin-name">
原名称:
<span>{{originName}}</span>
</p>
<p class="setting-name">
自定义名称:
<input type="text" v-model="settingName" />
</p>
<h5 class="setting-select-title">分类汇总</h5>
<div @click.stop class="select-wrap">
<span>选择一个函数:</span>
<div class="select-content">
<select class="select" v-model="selects">
<option
:data-id="item.id"
:data-label="item.label"
:data-value="item.value"
:value="item.label"
v-for="(item, index) in settings"
>{{item.value}}</option>
</select>
</div>
</div>
<div class="select-button">
<button @click.stop="confirm" class="confirm">确定</button>
<button @click.stop="cancel" class="cancel">取消</button>
</div>
</div>
</div>
<!-- 字段设置 end -->
</div>
</template>
<style lang="scss" scoped>
.report-wrap {
width: 100vw;
height: 100vh;
display: flex;
.report-container {
width: 70vw;
height: 100%;
background-color: aqua;
}
.select-container {
box-sizing: border-box;
padding: 12px;
width: 25vw;
height: 100%;
h3.key-list-title {
color: rgb(56, 141, 145);
margin-bottom: 4px;
}
.key-list {
ul.key {
background-color: #fff;
height: 355px;
overflow: hidden;
margin-top: 6px;
display: flex;
flex-flow: column;
li {
padding: 6px;
cursor: -webkit-grab;
}
li:hover {
background-color: rgb(230, 230, 230);
}
}
}
.data-view-content {
margin-top: 20px;
box-sizing: border-box;
.view {
box-sizing: border-box;
display: flex;
justify-content: space-around;
.other {
height: 150px;
background-color: #fff;
overflow-y: auto;
ul {
box-sizing: border-box;
padding: 2px;
li {
position: relative;
box-sizing: border-box;
padding: 2px;
border: 1px solid rgb(230, 230, 230);
background-color: #fff;
margin-bottom: 2px;
color: rgb(102, 102, 102);
font-size: 14px;
}
li:last-child {
margin-bottom: 0;
}
}
}
.other:last-child {
margin-bottom: 0;
}
.same {
height: 150px;
background-color: #fff;
overflow-y: auto;
ul {
box-sizing: border-box;
padding: 2px;
li {
position: relative;
box-sizing: border-box;
padding: 2px;
border: 1px solid rgb(230, 230, 230);
background-color: #fff;
margin-bottom: 2px;
color: rgb(102, 102, 102);
font-size: 14px;
&:after {
content: '';
position: absolute;
top: 10px;
right: 5px;
width: 0;
height: 0;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
border-top: 4px solid rgb(102, 102, 102);
}
}
li:last-child {
margin-bottom: 0;
}
}
}
.same:last-child {
margin-right: 0;
}
.same-wrap {
margin-right: 10px;
}
.same-wrap:last-child {
margin-right: 0;
}
.select-view-wrap {
width: calc(50%);
}
.row-view-wrap {
width: calc(50%);
}
.what-view-wrap {
width: calc(50%);
}
}
}
.update {
padding: 10px;
}
}
/*css代码*/
#menu {
width: 0; /*设置为0 隐藏自定义菜单*/
height: auto;
overflow: hidden; /*隐藏溢出的元素*/
box-shadow: 0 1px 1px #888, 1px 0 1px #ccc;
position: absolute; /*自定义菜单相对与body元素进行定位*/
background-color: #fff;
z-index: 100;
.menu {
width: 130px;
height: 25px;
line-height: 25px;
padding: 0 10px;
cursor: pointer;
}
.menu:hover {
background-color: rgb(230, 230, 230);
}
.disabled {
width: 130px;
height: 25px;
line-height: 25px;
padding: 0 10px;
color: rgb(187, 187, 187);
pointer-events: none;
cursor: default;
}
}
// setting 字段设置
.setting {
position: absolute;
box-sizing: border-box;
top: 50%;
left: 50%;
z-index: 100;
margin-top: -267px;
margin-left: -240px;
width: 480px;
height: 534px;
background-color: rgb(240, 240, 240);
.title {
background-color: #fff;
height: 25px;
line-height: 25px;
text-indent: 25px;
}
.setting-content {
box-sizing: border-box;
padding: 7px;
.origin-name {
padding: 5px 0 5px 0;
}
.setting-name {
padding: 5px 0 5px 0;
}
.setting-select-title {
padding: 5px 0 5px 0;
border-bottom: 1px solid rgb(160, 160, 160);
}
.select-wrap {
padding: 30px 0 0 30px;
.select-content {
margin-top: 10px;
width: 150px;
height: 30px;
select {
display: inline-block;
width: 100%;
height: 100%;
}
}
}
}
.select-button {
padding: 10px;
float: right;
button {
outline: none;
width: 80px;
height: 30px;
&.confirm {
background-color: rgb(229, 241, 251);
border: 1px solid rgb(0, 120, 215);
}
&.cancel {
margin-left: 10px;
}
}
}
}
}
</style>
参数一一说明
要将元素从一个列表拖到另一个列表中,两个列表必须具有相同的group值。您还可以定义列表是否可以放弃,给予和保留副本(clone)以及接收元素。
- name
string
-- group name - pull
true|false|["foo", "bar"]|'clone'|function
表示可以从列表中移出 - put
true|false|["baz", "qux"]|function
是可以从其他列表中添加元素,还是可以从中添加元素的组名数组 - revertClone:boolean—在移动到另一个列表后,将克隆的元素恢复到初始位置
dragUlKey() {
const ulKey = document.getElementById('key')
const self = this
new Sortable(ulKey, {
group: { name: 'shared', pull: 'clone', put: false },
animation: 150,
sort: false, // To disable sorting: set sort to false,
// Element dragging ended
onEnd: function(/**Event*/ evt) {
// console.log(
// "evt.item.innerHTML====>",
// evt.item.innerHTML,
// "evt.item.dataset.value====>",
// evt.item.dataset.value,
// "evt.item====>",
// evt.item,
// "evt.oldIndex====>",
// evt.oldIndex,
// "evt.item.children[0]====>",
// evt.item.children[0],
// "evt.item.children[0].children[0]====>",
// evt.item.children[0].children[0],
// "evt.item.parentNode====>",
// evt.item.parentNode,
// "evt.to====>",
// evt.to,
// "evt.target====>",
// evt.target,
// "evt===>",
// evt
// // );
},
})
},
我们在加上 revertClone: true
pull, put 可以为 funtion 这个就扩大了拖拽放置的功能了,显得没那么死板,为什么呢?我们可以根据自己需要来判断自己拖拽的项目需要放置在那个盒子里面,不许放置在那个盒子里面,或者说那个盒子不允许 clone,那个盒子允许 clone
dragSelectView() {
const ulKey = document.querySelector('.select-view ul.select')
const self = this
new Sortable(ulKey, {
animation: 150,
group: {
name: 'shared',
put: function(to, from, target) {
for (let i = 0; i < to.el.childNodes.length; i++) {
if (to.el.childNodes[i].dataset.value === target.dataset.value) {
return false
}
}
return true
},
},
sort: true,
removeOnSpill: true, // Enable plugin
onSpill: function(evt) {
console.log('evt.to===>', evt.to, evt.from)
if (evt.to.dataset.id === evt.from.dataset.id) {
this.options.removeOnSpill = true
} else {
this.options.removeOnSpill = false
}
},
onEnd: function(evt) {
console.log('evt update====>dragSelectView', evt.item)
},
onUpdate: function(evt) {},
})
},
当我们重复拖拽同一个项目的时候,只允许放置一次
sort: true 表示可以排序,sort: false 表示不能排序
delay: number 定义排序开始时间的时间(以毫秒为单位)。不幸的是,由于浏览器的限制,使用本地拖放功能无法在IE或Edge上进行延迟
Sortable.create(list, {
delay: 400
});
似乎不让我拖拽
delayOnTouchOnly 选项
是否仅在用户使用触摸(例如,在移动设备上)时才应用延迟。在任何其他情况下,都不会延迟。默认为false
swapThreshold 选项
交换区域将占据的目标百分比,介于0和之间1
invertSwap 选项
设置为true,将交换区域设置在目标的侧面,以实现“在项目之间”排序的效果
direction 选项
方向可排序应该排序]。可设置'vertical','horizontal'或功能,只要目标拖过将被调用。必须返回'vertical'或'horizontal'
Sortable.create(el, {
direction: function(evt, target, dragEl) {
if (target !== null && target.className.includes('half-column') && dragEl.className.includes('half-column')) {
return 'horizontal';
}
return 'vertical';
}
});
touchStartThreshold 选项
此选项类似于fallbackTolerance选项。
delay设置此选项后,即使手指不动,某些具有非常灵敏的触摸显示屏的手机(如三星Galaxy S8)也会触发不需要的触摸移动事件,从而导致排序不会触发。
此选项设置取消延迟排序之前必须发生的最小指针移动。
3到5之间的值是好的
disabled 选项
如果设置为,则禁用可排序true
var sortable = Sortable.create(list);
document.getElementById("switcher").onclick = function () {
var state = sortable.option("disabled"); // get
sortable.option("disabled", !state); // set
};
handle 选项
为了使列表项可拖动,Sortable可禁用用户的文本选择。这并不总是可取的。要允许选择文本,请定义一个拖动处理程序,该处理程序是每个列表元素都可以拖动的区域
Sortable.create(el, {
handle: ".my-handle"
});
<ul>
<li><span class="my-handle">::</span> list item text one
<li><span class="my-handle">::</span> list item text two
</ul>
.my-handle {
cursor: move;
cursor: -webkit-grabbing;
}
filter 选项
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)
.ghost {
opacity: 0.4;
}
Sortable.create(list, {
ghostClass: "ghost"
});
当拖拽的时候会出现背景颜色
chosenClass 选项
所选项目的类名称(默认sortable-chosen)
.chosen {
color: #fff;
background-color: #c00;
}
Sortable.create(list, {
delay: 500,
chosenClass: "chosen"
});
forceFallback 选项
如果设置为true,即使我们使用的是HTML5浏览器,也会使用非HTML5浏览器的后备广告。这使我们可以测试较旧浏览器的行为,甚至在较新的浏览器中,也可以使桌面浏览器,移动浏览器和旧浏览器之间的拖放感觉更加一致。
最重要的是,Fallback始终会生成该DOM元素的副本,并附加fallbackClass在选项中定义的类。此行为控制此“拖动”元素的外观
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css"/>
<!-- Latest Sortable -->
<script src="https://raw.githack.com/SortableJS/Sortable/master/Sortable.js"></script>
<div id="sortable" class="grid">
<div class="item"><img src="https://loremflickr.com/100/100?random=1" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=2" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=3" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=4" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=5" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=6" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=7" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=8" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=9" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=10" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=11" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=12" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=13" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=14" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=15" alt=""></div>
<div class="item"><img src="https://loremflickr.com/100/100?random=16" alt=""></div>
</div>
</body>
</html>
// style
body {
padding: 20px;
}
.grid .item {
padding: 10px;
float: left;
}
.grid .item img {
cursor: move;
cursor: -webkit-grabbing;
}
.sortable-fallback img{
border-radius: 50%;
}
// js
Sortable.create(sortable, {
forceFallback: true
});
fallbackTolerance 选项
模拟本地拖动阈值。指定以像素为单位的鼠标在被视为拖动之前应移动的距离。如果项目也可以单击,例如在链接列表中,则很有用。
当用户在可排序元素内单击时,在按下和松开之间,您的手通常会略微移动。仅当您将指针移过一定的公差时才开始拖动,这样您就不会在每次单击时意外开始拖动。
3到5可能是不错的值
dragoverBubble 选项
如果设置为true,则拖动事件将冒泡到父可排序对象。适用于后备事件和本机拖动事件。默认情况下为false,但是Sortable仅在将元素插入父Sortable或可以插入父Sortable时才停止冒泡事件,但不在特定时间(由于动画等) 。
从1.8.0开始,您可能希望将此选项保留为false。在1.8.0之前,它可能需要true嵌套可排序项才能起作用
removeCloneOnHide 选项
如果设置为false,则通过将其CSS display属性设置为来隐藏克隆none。默认情况下,此选项为true,这意味着Sortable在应该被隐藏时将从DOM中删除克隆的元素
emptyInsertThreshold 选项
拖动时鼠标必须与一个空的可排序对象之间的距离(以像素为单位),以便将拖动元素插入到该可排序对象中。默认为5。设置为0禁用此功能
Event object (demo)
- to:HTMLElement — list, in which moved element
- from:HTMLElement — previous list
- item:HTMLElement — dragged element
- clone:HTMLElement
- oldIndex:Number|undefined — old index within parent
- newIndex:Number|undefined — new index within parent
- oldDraggableIndex: Number|undefined — old index within parent, only counting draggable elements
- newDraggableIndex: Number|undefined — new index within parent, only counting draggable elements
- pullMode:String|Boolean|undefined — Pull mode if dragging into another sortable ("clone", true, or false), otherwise undefined
onEnd: function(/**Event*/ evt) {
console.log(
"evt.item.innerHTML====>",
evt.item.innerHTML,
"evt.item.dataset.value====>",
evt.item.dataset.value,
"evt.item====>",
evt.item,
"evt.oldIndex====>",
evt.oldIndex,
"evt.item.parentNode====>",
evt.item.parentNode,
"evt.to====>",
evt.to,
"evt.target====>",
evt.target,
"evt===>",
evt
);
},
move event object
- to:HTMLElement
- from:HTMLElement
- dragged:HTMLElement
- draggedRect:DOMRect
- related:HTMLElement — element on which have guided
- relatedRect:DOMRect
- willInsertAfter:Boolean — true if will element be inserted after target (or false if before)
Method
- option(name:String[, value:]):
Get or set the option.
- closest(el:String[, selector:HTMLElement]):HTMLElement|null
For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.
- toArray():String[]
Serializes the sortable's item data-id's (dataIdAttr option) into an array of string.
- sort(order:String[])
Sorts the elements according to the array.
var order = sortable.toArray();
sortable.sort(order.reverse()); // apply
插件介绍
- OnSpill Plugins
该文件包含两个单独的插件,RemoveOnSpill和RevertOnSpill。它们可以单独导入,也可以将默认导出(两个插件的数组)传递给Sortable.mount它们。
这些插件是默认插件,并包含在Sortable的默认UMD和ESM版本中
import { Sortable, OnSpill } from 'sortablejs/modular/sortable.core.esm';
Sortable.mount(OnSpill);
RevertOnSpill Plugin
此插件启用后,如果溢出,将导致拖动的项目恢复到其原始位置(即,将其拖放到有效的Sortable放置目标之外)
new Sortable(el, {
revertOnSpill: true, // Enable plugin
// Called when item is spilled
onSpill: function(/**Event*/evt) {
evt.item // The spilled item
}
});
RemoveOnSpill Plugin
启用此插件后,如果溢出,则将导致从DOM中删除被拖动的项目(即,将其拖放到有效的Sortable放置目标之外)
new Sortable(el, {
removeOnSpill: true, // Enable plugin
// Called when item is spilled
onSpill: function(/**Event*/evt) {
evt.item // The spilled item
}
});
自动滚屏
此插件可让页面在移动设备和IE9上可滚动元素的边缘附近拖动时(或在启用回退时)自动滚动,并且还增强了大多数浏览器的本机拖放自动滚动。演示:
import { Sortable, AutoScroll } from 'sortablejs';
Sortable.mount(new AutoScroll());
Options
new Sortable(el, {
scroll: true, // Enable the plugin. Can be 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, speed of the scrolling
bubbleScroll: true // apply autoscroll to all parent elements, allowing for easier movement
});
scroll 选项
启用插件。默认为true。也可以将其设置为HTMLElement,这将是自动滚动的基础
scrollFn 选项
定义将用于自动滚动的功能。默认情况下使用el.scrollTop / el.scrollLeft。当您具有带有专用滚动功能的自定义滚动条时很有用。'continue'如果希望允许Sortable的本机自动滚动,则应返回此函数
scrollSensitivity 选项
定义鼠标必须靠近边缘才能开始滚动
scrollSpeed 选项
鼠标指针进入该scrollSensitivity距离内时窗口滚动的速度。
bubbleScroll 选项
如果设置为true,则普通autoscroll功能还将应用于用户拖动的元素的所有父元素
// html
<body>
<div id="content" class="outer">
<div class="inner-semi">
<div id="things" class="inner smaller">
</div>
</div>
<div id="things2" class="inner">
</div>
<div id="things3" class="inner">
</div>
<div id="things4" class="inner">
</div>
</div>
<script src="https://raw.githack.com/SortableJS/Sortable/master/Sortable.js"></script>
</body>
// js
var things = document.getElementById('things');
var things2 = document.getElementById('things2');
var things3 = document.getElementById('things3');
var things4 = document.getElementById('things4');
var options = {
group: 'a',
ghostClass: 'ghost',
forceFallback: true,
scroll: true,
bubbleScroll: true
};
Sortable.create(things, options)
Sortable.create(things2, options)
Sortable.create(things3, options)
Sortable.create(things4, options)
for (let i = 0; i < 20; i++) {
things.innerHTML = things.innerHTML +
('<div class="card">Lorem ipsum dolor sit amet 1, ' + i + '</div>');
things2.innerHTML = things2.innerHTML +
('<div class="card">Lorem ipsum dolor sit amet 2, ' + i + '</div>');
things3.innerHTML = things3.innerHTML +
('<div class="card">Lorem ipsum dolor sit amet 3, ' + i + '</div>');
things4.innerHTML = things4.innerHTML +
('<div class="card">Lorem ipsum dolor sit amet 4, ' + i + '</div>');
}
// css
.outer {
height: auto;
max-width: 50vw;
white-space: nowrap;
overflow-x: auto;
padding: 1em;
background-color: blue;
margin-bottom: 200vh;
}
.inner-semi {
display: inline-block;
height: 60vh;
overflow-y: auto;
background-color: aqua;
}
.inner {
display: inline-block;
max-height: 50vh;
overflow-y: auto;
background-color: white;
padding: 0.5em;
margin: 1em;
}
.smaller {
max-height: 20vh;
margin-bottom: 50vh;
}
.card {
padding: 1em;
background-color: #EEE;
margin: 0.3em;
}
.ghost {
color: #CCC;
background-color: #CCC;
}
.drag {
pointer-events: none;
}
交换插件
该插件修改了Sortable的行为,以允许项目彼此交换而不是进行排序。一旦开始拖动,用户就可以将其拖动到其他项目上,并且元素不会发生任何变化。但是,用户放置的项目将与原始拖动的项目交换
import { Sortable, Swap } from 'sortablejs/modular/sortable.core.esm';
Sortable.mount(new Swap());
new Sortable(el, {
swap: true, // Enable swap mode
swapClass: "sortable-swap-highlight" // Class name for swap item (if swap mode is enabled)
});
本文使用 mdnice 排版