Cut the nonsense, go directly to the code:
Example code 1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>js拖拽排排序1</title>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
width: 90vw;
height: 80vh;
}
.list {
width: 500px;
height: 335px;
}
.list-item {
width: 100%;
height: 50px;
background-color: rgb(11, 137, 11);
margin: 5px 0;
border-radius: 5px;
cursor: move;
user-select: none;
}
.list-item.moving {
background: transparent;
color: transparent;
border: 1px dashed #333;
transition: all 0.3s linear;
}
</style>
</head>
<body>
<div class="list">
<div draggable="true" class="list-item">1</div>
<div draggable="true" class="list-item">2</div>
<div draggable="true" class="list-item">3</div>
<div draggable="true" class="list-item">4</div>
<div draggable="true" class="list-item">5</div>
<div draggable="true" class="list-item">6</div>
</div>
<script>
const list = document.querySelector('.list')
// 目前正在拖动的元素
let sourceNode
// 拖动元素
list.ondragstart = (e) => {
setTimeout(() => {
e.target.classList.add('moving')
}, 0)
sourceNode = e.target
e.dataTransfer.effectAllowed = 'move'
}
// 拖动元素放置到目标上
list.ondragover = (e) => {
e.preventDefault()
}
// 拖动元素进入放置目标
list.ondragenter = (e) => {
e.preventDefault()
if (e.target === list || e.target === sourceNode) return
console.log(e.target)
// 拿到父元素的所有子元素,并转换为数组
const children = Array.from(list.children)
// 找到正在拖动元素的下标
const sourceIndex = children.indexOf(sourceNode)
// console.log('拖动元素的下标', sourceIndex)
// 找到目标元素的下标
const targetIndex = children.indexOf(e.target)
console.log('目标元素的下标', targetIndex)
// 比较拖动元素和目标元素的下标来判断是向上拖动还是向下拖动
if (sourceIndex < targetIndex) {
console.log('向下拖动')
// 拖动元素下标比目标元素小,插入到目标元素的后面
list.insertBefore(sourceNode, e.target.nextElementSibling)
} else {
console.log('向上拖动')
// 拖动元素下标比目标元素大,插入到目标元素的前面
list.insertBefore(sourceNode, e.target)
}
}
// 拖动完成
list.ondragend = (e) => {
e.target.classList.remove('moving')
}
</script>
<script src="./flip.js"></script>
</body>
</html>
Example code 2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
list-style: none;
margin: 0;
padding: 0;
}
#container {
width: 500px;
margin: 100px auto;
}
.ele {
width: 100%;
height: 40px;
border: 1px solid #999;
background: cadetblue;
margin-top: 2px;
border-radius: 10px;
padding-left: 10px;
color: white;
cursor: move;
}
</style>
</head>
<body>
<ul id="container">
<li class="ele" draggable="true">1</li>
<li class="ele" draggable="true">2</li>
<li class="ele" draggable="true">3</li>
<li class="ele" draggable="true">4</li>
</ul>
<script>
var node = document.querySelector('#container')
var draging = null
//使用事件委托,将li的事件委托给ul
node.ondragstart = function (event) {
//console.log("start");
//firefox设置了setData后元素才能拖动!!!!
//event.target出发事件的元素
event.dataTransfer.setData('te', event.target.innerText) //不能使用text,firefox会打开新tab
//event.dataTransfer.setData("self", event.target);
draging = event.target
console.log('draging', draging)
}
node.ondragover = function (event) {
//console.log("onDrop over");
//取消默认行为
event.preventDefault()
var target = event.target
console.log('target', target)
//因为dragover会发生在ul上,所以要判断是不是li
if (target.nodeName === 'LI') {
if (target !== draging) {
//getBoundingClientRect()用于获取某个元素相对于视窗的位置集合
var targetRect = target.getBoundingClientRect()
console.log('targetRect', targetRect)
var dragingRect = draging.getBoundingClientRect()
console.log('dragingRect', dragingRect)
if (target) {
if (target.animated) {
return
}
}
if (_index(draging) < _index(target)) {
//nextSibling 属性可返回某个元素之后紧跟的节点(处于同一树层级中)。
target.parentNode.insertBefore(draging, target.nextSibling)
} else {
target.parentNode.insertBefore(draging, target)
}
_animate(dragingRect, draging)
_animate(targetRect, target)
}
}
}
//获取元素在父元素中的index
function _index(el) {
var index = 0
if (!el || !el.parentNode) {
return -1
}
//previousElementSibling属性返回指定元素的前一个兄弟元素(相同节点树层中的前一个元素节点)。
while (el && (el = el.previousElementSibling)) {
//console.log(el);
index++
}
return index
}
function _animate(prevRect, target) {
var ms = 300
if (ms) {
var currentRect = target.getBoundingClientRect()
//nodeType 属性返回以数字值返回指定节点的节点类型。1=元素节点 2=属性节点
if (prevRect.nodeType === 1) {
prevRect = prevRect.getBoundingClientRect()
}
_css(target, 'transition', 'none')
_css(target, 'transform', 'translate3d(' + (prevRect.left - currentRect.left) + 'px,' + (prevRect.top - currentRect.top) + 'px,0)')
target.offsetWidth // 触发重绘
//放在timeout里面也可以
// setTimeout(function() {
// _css(target, 'transition', 'all ' + ms + 'ms');
// _css(target, 'transform', 'translate3d(0,0,0)');
// }, 0);
_css(target, 'transition', 'all ' + ms + 'ms')
_css(target, 'transform', 'translate3d(0,0,0)')
clearTimeout(target.animated)
target.animated = setTimeout(function () {
_css(target, 'transition', '')
_css(target, 'transform', '')
target.animated = false
}, ms)
}
}
//给元素添加style
function _css(el, prop, val) {
var style = el && el.style
if (style) {
if (val === void 0) {
//使用DefaultView属性可以指定打开窗体时所用的视图
if (document.defaultView && document.defaultView.getComputedStyle) {
val = document.defaultView.getComputedStyle(el, '')
} else if (el.currentStyle) {
val = el.currentStyle
}
return prop === void 0 ? val : val[prop]
} else {
if (!(prop in style)) {
prop = '-webkit-' + prop
}
style[prop] = val + (typeof val === 'string' ? '' : 'px')
}
}
}
</script>
</body>
</html>