Sortable.js: A powerful JavaScript drag and drop library

Original address: Sortable.js: A powerful JavaScript drag and drop library

1. Introduction

Sortable.js is a powerful JavaScript drag and drop library! ! ! Used to create draggable and sortable elements on web pages. It provides a simple yet powerful API that enables developers to easily implement drag-and-drop functionality and allows users to reorder lists, grids, and other elements via drag-and-drop.

It mainly has the following characteristics:

  • Good compatibility: supports touch screen devices and most browsers, supports modern browsers, and provides backward compatibility for older browsers;
  • Simple: Simple API, easy to use, through simple HTML structure and JavaScript API, you can quickly implement drag-and-drop sorting function;
  • Native: Based on the drag-and-drop API in native HTML5;
  • CSS framework compatibility: supports all css frameworks, like Bootstrap;
  • Zero dependencies: no dependence on other frameworks such as Jquery;
  • SPA has good support: supports multiple frameworks (angular, vue, react, etc.);
  • Highly customizable: drag-and-drop behavior and sorting effects can be customized through configuration options and callback functions;
  • Interactivity: In addition to basic drag-and-drop functionality, SortableJS also provides rich interactivity options, such as drag-and-drop animation, sort synchronization, etc.; Community support:
  • SortableJS has an active developer community that provides support, plugins, and examples for developers using the library.

2. Installation

1. npm installation

npm install sortablejs --save

2. bower installation

bower install --save sortablejs

3. Script introduction

<script src="../../js/Sortable.min.js"></script>

3. Configuration items

const sortable = new Sortable(el, {
    
    
     // or { name: "...", pull: [true, false, 'clone', array], put: [true, false, array] }
    group: "name", 
    // boolean 定义是否列表单元是否可以在列表容器内进行拖拽排序
    sort: true,  
     // number 定义鼠标选中列表单元可以开始拖动的延迟时间;
    delay: 0,
    // px,在取消延迟拖动事件之前,点应该移动多少像素
    touchStartThreshold: 0, 
    // boolean 定义是否此sortable对象是否可用,为true时sortable对象不能拖放排序等功能,为false时为可以进行排序,相当于一个开关;
    disabled: false, 
    // @see Store
    store: null, 
     // ms, number 单位:ms,定义排序动画的时间
    animation: 150, 
    // 制作动画。默认为null,https://easings.net/。
    easing: "cubic-bezier(1, 0, 0, 1)",
     // 格式为简单css选择器的字符串,使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动
    handle: ".my-handle", 
    // 过滤器,不需要进行拖动的元素
    filter: ".ignore-elements", 
    //  在触发过滤器`filter`的时候调用`event.preventDefault()`
    preventOnFilter: true, 
     // 允许拖拽的项目类名
    draggable: ".item", 
     // drop placeholder的css类名
    ghostClass: "sortable-ghost", 
     // 被选中项的css 类名
    chosenClass: "sortable-chosen", 
     // 正在被拖拽中的css类名
    dragClass: "sortable-drag", 
    dataIdAttr: 'data-id',
    // 交换区的阈值
    swapThreshold: 1,
    // 如果设置为 true,将始终使用反向交换区
    invertSwap: false, 
    // 反向交换阈值,默认情况下将设置为swapThreshold 值
    invertedSwapThreshold: 1, 
    // 拖拽方向 (默认情况下会自动判断方向)
    direction: 'horizontal', 
    // 忽略 HTML5拖拽行为,强制回调进行
    forceFallback: false,  
    // 当使用forceFallback的时候,被复制的dom的css类名
    fallbackClass: "sortable-fallback",  
    // 将cloned DOM 元素挂到body元素上。
    fallbackOnBody: false,  
     // 以像素为单位指定鼠标在被视为拖动之前应移动的距离。
    fallbackTolerance: 0,
    // 是否启用滚动,值为true或者HTMLElement
    scroll: true,
    // 自定义滚动
    scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) {
    
     ... }, 
    // 鼠标必须离边缘多近才能开始滚动,单位 px
    scrollSensitivity: 30, 
     // 滚动速度,px
    scrollSpeed: 10,
    // 将自动滚动应用于所有父元素,以便更轻松地移动
    bubbleScroll: true, 
    dragoverBubble: false,
    // 删除不显示的克隆元素,而不是仅仅隐藏它
    removeCloneOnHide: true, 
    // 拖动时鼠标必须与空可排序项的距离(以像素为单位),以便将拖动元素插入到该可排序项中, 设置为0禁用此功能。
    emptyInsertThreshold: 5,

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

    // 元素被选中
    onChoose: function (/**Event*/evt) {
    
    
        evt.oldIndex;  // element index within parent
    },

    // 元素未被选中的时候(从选中到未选中)
    onUnchoose: function(/**Event*/evt) {
    
    
        // same properties as onEnd
    },

    // 开始拖拽的时候
    onStart: function (/**Event*/evt) {
    
    
        evt.oldIndex;  // element index within parent
    },

    // 结束拖拽
    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.clone // the clone element
        evt.pullMode;  // when item is in another sortable: `"clone"` if cloning, `true` if moving
    },

    // 元素从一个列表拖拽到另一个列表
    onAdd: function (/**Event*/evt) {
    
    
        // same properties as onEnd
    },

    // 列表内元素顺序更新的时候触发
    onUpdate: function (/**Event*/evt) {
    
    
        // same properties as onEnd
    },

    // 列表的任何更改都会触发
    onSort: function (/**Event*/evt) {
    
    
        // same properties as onEnd
    },

    // 元素从列表中移除进入另一个列表
    onRemove: function (/**Event*/evt) {
    
    
        // same properties as onEnd
    },

    // 试图拖拽一个filtered的元素
    onFilter: function (/**Event*/evt) {
    
    
        var itemEl = evt.item;  // HTMLElement receiving the `mousedown|tapstart` event.
    },

    // 拖拽移动的时候
    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
    },

    // clone一个元素的时候触发
    onClone: function (/**Event*/evt) {
    
    
        var origEl = evt.item;
        var cloneEl = evt.clone;
    },

    // 拖拽元素改变位置的时候
    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
    }
});                    

1.Attributes

(1)group:string or object

  • string : Naming, I personally recommend using the element id. It is used to set up a drag-and-drop container. It will be introduced in the put settings in the array.
  • object : {name, pull, put}.
    name : Same method as string.
    pull : pull is used to define the settings that are moved out of this list container, true/false/'clone'/function. true: The list unit in the list container can be moved out; false: The list unit in the list container cannot be moved out; 'clone': The list unit is moved out, and the moved is a copy of the element; function: Function judgment used for pull , you can perform complex logic and return false/true in the function to determine whether to move out.
    put : put is used to define the setting of placing list cells into this list container, true/false/['foo','bar']/function. true: The list container can put list units from other list containers; false: The opposite of true; ['foo','bar']: This can be a string or an array of strings, representing the group configuration item The name value defined in; function: used to judge the function of put, which can perform complex logic. Return false/true in the function to judge whether to put it in.

(2) sort : boolean defines whether the list unit can be dragged and sorted within the list container;

(3)delay : number defines the delay time before the mouse selects the list unit and starts dragging;

(4) disabled : boolean defines whether this sortable object is available. When it is true, the sortable object cannot be dragged and dropped for sorting and other functions. When it is false, it can be sorted, which is equivalent to a switch;

(5)animation : number unit: ms, defines the time of sorting animation;

(6) handle : selector format is a string of simple css selector, so that the elements in the list unit that match the selector become the drag handle. Only by pressing and holding the drag handle can the list unit be dragged;

**(7)filter: **selector is a string in the format of a simple CSS selector, defining which list units cannot be dragged and dropped. It can be set to multiple selectors, separated by ",";

(8) draggable : selector format is a string of simple css selector, defining which list units can be dragged and dropped

(9)ghostClass : The selector format is a string of simple css selector. When dragging the list unit, a copy will be generated as a shadow unit to simulate the sorting of the dragged unit. This configuration item is to add a shadow unit to the class, we can use this method to edit styles for shadow elements;

(10)chosenClass : The selector format is a string of simple css selector. When a list unit is selected, a class will be added to the unit;

(11)forceFallback : boolean If set to true, native HTML5 drag and drop will not be used, and the styles of some elements in drag and drop can be modified;

(12)fallbackClass : string When forceFallback is set to true, the style of the mouse attached unit during drag and drop;

(13)scroll : boolean defaults to true. When the sorted container is a scrollable area, dragging and dropping can cause the area to scroll.

2.Event

onChoose: function callback function when the list unit is selected

onStart: function callback function for the start of dragging the list unit

onEnd: function callback function after drag and drop of list unit ends

onAdd: function callback function for adding a list unit to this list container

onUpdate: function callback function after the sorting of list cells in the list container changes.

onRemove: function callback function for moving list elements to another list container

onFilter: function callback function that attempts to select a list cell filtered by filter

onMove: function callback function when moving a list unit in a list container or multiple list containers

onClone: ​​function callback function when creating a copy of a list cell

3.Event object

The event objects are slightly different in each function. You can view the properties of the object through the output object. Here are a few:

to: HTMLElement – ​​move to list container

from: HTMLElement – ​​list container of source

item: HTMLElement – ​​the moved list unit

clone: ​​HTMLElement – ​​the list unit of the copy

oldIndex: number/undefined – the original number in the list container

newIndex: number/undefined – the new serial number in the list container

4.Method

option(name[,value])
gets or sets the item parameter. The usage method is similar to jQuery usage. If there is no second parameter, the value corresponding to the first parameter in option is obtained. When there is a second parameter, it will be reassigned. The value corresponding to the first parameter;

closest
didn't understand

toArray()
serializes the data-id of the sortable list unit (can be modified through dataIdAttr in the configuration item) into an array, and returns it to this array

sort()
sorts the list cells through the array of data-id of the custom list cell

save()
destroy()

4. Practical application

1.Vue first renders the data to the page;

2. Use sortablejs to operate the real dom and get the list. The core of sortablejs is to operate the real dom. At this time, because the data of Vue itself has not been updated, it will not trigger the re-rendering of the dom. This leads to the difference between the data in Vue and the data of the real dom. It doesn’t match up, so there is step 3;

3. Use the onEnd method of sortablejs to map the data changes to the real Vue data source list, so that the DOM changes during dragging are corresponding.

The environment is as follows:
Insert image description here

1. Simple list drag and drop

<script setup lang="ts">
import Sortable from 'sortablejs'
import {
    
     ref, onMounted, nextTick } from 'vue';

const sortableRef = ref(null)
const list = [
  {
    
    
    name: 'item1'
  },
  {
    
    
    name: 'item2'
  },
  {
    
    
    name: 'item3'
  },
  {
    
    
    name: 'item4'
  },
  {
    
    
    name: 'item5'
  },
  {
    
    
    name: 'item6'
  }
]

const initSortable = () => {
    
    
  new Sortable(sortableRef.value, {
    
    
      delay: 10,
      animation: 150,
      // 如果有不想排序的,在这里按照class匹配的方式写出即可
      filter: '.demo-title',
      // 需要拖拽的标签的class,强烈推荐写
      draggable: '.demo-item',
      // drop placeholder的css类名
      ghostClass: 'blue-background-class'
  });
}

onMounted(() => {
    
    
  nextTick(() => {
    
    
    initSortable()
  })
})
</script>

<template>
  <ul ref="sortableRef" class="demo">
    <li class="demo-title">Demo</li>
    <li class="demo-item" v-for="item in list" :key="item.name">{
    
    {
    
     item.name }}</li>
  </ul>
</template>

<style lang="scss" scoped>
.demo {
    
    
  list-style: none;
  width: 40%;
  margin: 200px auto 0;
  .demo-title {
    
    
    text-align: center;
    font-weight: 600;
    font-size: 16px;
  }
  .demo-item {
    
    
    border: 1px solid red;
    margin: 10px 0;
    padding: 10px 0;
    text-align: center;
  }
}
.blue-background-class {
    
    
  background-color: #1890ff;
}
</style>

The effect is as follows:
Insert image description here

2. Drag and drop multiple lists to each other

<script setup lang="ts">
import Sortable from 'sortablejs'
import {
    
     ref, onMounted, nextTick } from 'vue';

const demo1Ref = ref(null)
const demo2Ref = ref(null)

const initSortable = () => {
    
    
  new Sortable(demo1Ref.value, {
    
    
    // 相互拖拽列表的组名必须一样
    group: 'shared',
    delay: 10,
    animation: 150,
    // 需要拖拽的标签的class,强烈推荐写
    // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
    draggable: '.demo-item'
  });

  new Sortable(demo2Ref.value, {
    
    
    // 相互拖拽列表的组名必须一样
    group: 'shared',
    delay: 10,
    animation: 150,
    // 需要拖拽的标签的class,强烈推荐写
    // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
    draggable: '.demo-item'
  });
}

onMounted(() => {
    
    
  nextTick(() => {
    
    
    initSortable()
  })
})
</script>

<template>
  <div class="demo-container">
    <ul ref="demo1Ref" class="demo">
      <li class="demo-item demo1-item" v-for="item in 6" :key="'demo1'+item">item{
    
    {
    
     item }}</li>
    </ul>
    <ul ref="demo2Ref" class="demo">
      <li class="demo-item demo2-item" v-for="item in 6" :key="'demo2'+item">item{
    
    {
    
     item }}</li>
    </ul>
  </div>
</template>

<style lang="scss" scoped>
.demo-container {
    
    
  display: flex;
  justify-content: space-between;
  width: 1000px;
  .demo {
    
    
    list-style: none;
    width: 48%;
    .demo-item {
    
    
      margin: 10px 0;
      padding: 10px 0;
      text-align: center;
    }
    .demo1-item {
    
    
      background-color: #5ac8fa;
    }
    .demo2-item {
    
    
      background-color: #c0ca33;
    }
  }
}
</style>        

The effect is as follows:
picture

3. Clone (copy nodes by dragging)

new Sortable(demo1Ref.value, {
    
    
  group: {
    
    
    name: 'shared',
    pull: 'clone' // To clone: set pull to 'clone'
  },
  delay: 10,
  animation: 150,
  // 需要拖拽的标签的class,强烈推荐写
  // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
  draggable: '.demo-item'
});

new Sortable(demo2Ref.value, {
    
    
  group: {
    
    
    name: 'shared',
    pull: 'clone' // To clone: set pull to 'clone'
  },
  delay: 10,
  animation: 150,
  // 需要拖拽的标签的class,强烈推荐写
  // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
  draggable: '.demo-item'
});

The effect is as follows:

picture
4. Sorting is prohibited

The list of coordinates cannot be sorted because sorting is disabled, but you can still drag from the left to the right.

new Sortable(demo1Ref.value, {
    
    
  group: {
    
    
    name: 'shared',
    pull: 'clone',
    put: false // 不允许拖拽进这个列表
  },
  sort: false, // 设为false,禁止sort
  delay: 10,
  animation: 150,
  // 需要拖拽的标签的class,强烈推荐写
  // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
  draggable: '.demo-item'
});

new Sortable(demo2Ref.value, {
    
    
  group: 'shared',
  delay: 10,
  animation: 150,
  // 需要拖拽的标签的class,强烈推荐写
  // 拖拽元素的类名一样,否则其它列表拖过来的不能在当前列表拖动
  draggable: '.demo-item'
});

The effect is as follows:

Insert image description here

5、Handle

Make the elements in the list unit that match the selector become the handles for dragging. Only by pressing and holding the drag handle can the list unit be dragged. In the face code, elements can only be dragged by holding down the white square.

<script setup lang="ts">
import Sortable from 'sortablejs'
import {
    
     ref, onMounted, nextTick } from 'vue';

const sortableRef = ref(null)

const initSortable = () => {
    
    
  new Sortable(sortableRef.value, {
    
    
    handle: '.handle-draggabel',
    animation: 150,
  });
}

onMounted(() => {
    
    
  nextTick(() => {
    
    
    initSortable()
  })
})
</script>

<template>
  <ul ref="sortableRef" class="demo">
    <li class="demo-item" v-for="item in 6" :key="item">
      <span class="handle-draggabel"></span>
      <span>item{
    
    {
    
     item }}</span>
    </li>
  </ul>
</template>

<style lang="scss" scoped>
.demo {
    
    
  list-style: none;
  width: 40%;
  .demo-item {
    
    
    background-color: #1890ff;
    margin: 10px 0;
    padding: 10px 0;
    color: #fff;
    text-align: center;
    .handle-draggabel {
    
    
      display: inline-block;
      width: 14px;
      height: 14px;
      background-color: #fff;
      margin-right: 10px;
      cursor: pointer;
    }
  }
}
</style>

The effect is as follows:

picture

5. Summary

In addition to basic drag-and-drop functionality, SortableJS also provides many additional features and options, such as drag-and-drop animation, sort synchronization, multi-select mode, and more. These features can help you create richer and more user-friendly interactive experiences.

SortableJS has a very active community, with many developers using the library and contributing plugins and examples to it. This means you can find many ready-made solutions and resources to help you solve the problems you encounter in development.

Overall, SortableJS is a powerful, flexible and easy-to-use JavaScript library suitable for various types of drag-and-drop sorting needs. Whether it's a simple list sorting or a complex interactive application, SortableJS can help you quickly achieve what you need. If you're looking for a reliable drag-and-drop library to enhance the interactivity of your web pages or applications, SortableJS is an option worth considering.

Document address: http://www.sortablejs.com/index.html

Guess you like

Origin blog.csdn.net/qq_39327418/article/details/135465718