转自:https://www.zhangxinxu.com/wordpress/2016/11/html5-drag-drop-module-insert-sort-delete-demo/
代码基于jQuery编写,可以省点事件。另外,兼容性是IE10+以及其他现代浏览器效果OK,不支持IE7-IE9浏览器,因为使用的是原生的HTML5 drag/drop API,一般适合开发一些工具后者内网项目的时候使用。如果对兼容性有要求,大家可以去搜一搜一些复杂的jQuery插件。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="description" content="基于HTML5 drag/drop的模块拖动插入排序和删除 » 张鑫旭-鑫空间-鑫生活" />
<meta name="description" content="张鑫旭web前端学习实例页面 基于HTML5 drag/drop的模块拖动插入排序和删除" />
<meta name="keywords" content="web前端, css, jQuery, javascript" />
<meta name="author" content="张鑫旭, zhangxinxu" />
<title>基于HTML5 drag/drop的模块拖动插入排序和删除 » 张鑫旭-鑫空间-鑫生活</title>
<style>
.demo {
padding-top: 30px;
}
.container {
background-color: #f0f3f9;
font-family: 'PingFang SC', 'Microsoft Yahei';
min-height: 610px;
position: relative;
overflow: hidden;
}
.module-ul {
width: 180px;
position: absolute; top: 0; bottom: 0; left: 0;
list-style: none;
margin: 0;
padding: 0;
background-color: #34538b;
}
.module-li {
margin: 20px auto;
padding: 10px 0;
background-color: #fff;
width: 150px;
text-align: center;
cursor: move;
position: relative;
}
.module-li:hover::before {
content: '';
position: absolute;
left: 0; right: 0; top: 0; bottom: 0;
padding: 10px;
background-color: rgba(0,0,0,.5);
}
.module-body {
margin-left: 180px;
}
.module-stage {
max-width: 90%;
min-width: 600px;
margin: 0 auto;
}
.module-placeholder {
height: 584px;
background-color: #fff;
position: relative;
margin: 20px 0;
}
.module-placeholder:empty::before {
position: absolute;
left: 50%;
top: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
color: #aaa;
content: '从左侧拖模块放这里';
font-size: 20px;
}
.module-placeholder.dragenter {
outline: 2px dashed #ddd;
}
.module-drag {
padding: 10px 0;
margin: 20px 0;
background-color: #fff;
text-align: center;
cursor: move;
position: relative;
}
.module-drag.dragstart {
opacity: .5;
}
.module-drag:hover::before {
content: '';
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
/* 模块拖拽删除 */
.module-remove {
position: absolute;
top: 0; bottom: 0; left: -180px;
width: 180px;
padding-top: 40px;
background-color: #34538b;
transition: left .25s, background-color .2s;
z-index: 1;
}
.module-remove.dragenter {
left: 0;
}
.module-remove-icon {
position: absolute;
font-size: 160px;
left: 50%; top: 0;margin-top: 100px;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
color: #fff;
}
.module-remove:hover,
.module-remove.dragover {
background-color: #F15656;
}
</style>
</head>
<body>
<!-- 演示区域 -->
<div class="container">
<ul id="module" class="module-ul">
<li class="module-li" draggable="true" data-url="https://i0.hippopx.com/photos/320/918/427/sky-clouds-sunlight-dark-preview.jpg">
<img class="module-img" src="https://i0.hippopx.com/photos/320/918/427/sky-clouds-sunlight-dark-thumb.jpg" height="96" alt="模块占位图1">
</li>
<li class="module-li" draggable="true" data-url="https://i0.hippopx.com/photos/477/768/839/swimmer-sport-swim-water-preview.jpg">
<img class="module-img" src="https://i0.hippopx.com/photos/477/768/839/swimmer-sport-swim-water-thumb.jpg" height="128" alt="模块占位图2">
</li>
<li class="module-li" draggable="true" data-url="https://i0.hippopx.com/photos/20/987/594/woman-young-rain-pond-preview.jpg">
<img class="module-img" src="https://i0.hippopx.com/photos/20/987/594/woman-young-rain-pond-thumb.jpg" height="96" alt="模块占位图3">
</li>
<li class="module-li" draggable="true" data-url="https://i0.hippopx.com/photos/740/837/204/spices-cinnamon-sticks-odor-aroma-preview.jpg">
<img class="module-img" src="https://i0.hippopx.com/photos/740/837/204/spices-cinnamon-sticks-odor-aroma-thumb.jpg" height="86" alt="模块占位图4">
</li>
</ul>
<div id="moduleRemove" class="module-remove"><span class="module-remove-icon">×</span></div>
<div id="moduleBody" class="module-body">
<div id="moduleStage" class="module-stage">
<div id="modulePlaHd" class="module-placeholder"></div>
</div>
</div>
</div>
<!-- 演示区域 结束 -->
<script src="http://apps.bdimg.com/libs/jquery/1.9.0/jquery.min.js"></script>
<script>
var moduleDrag = (function() {
// 模块操作类名,标记用
var CL_MODULE = 'jsDragModule';
// 状态类名
var CL_DRAGENTER = 'dragenter';
var CL_DRAGOVER = 'dragover';
var CL_DRAGSTART = 'dragstart';
return {
el: {},
store: {},
events: function() {
var self = this;
// 拖拽相关的事件
var el = self.el;
// 元素
var module = el.module, stage = el.stage;
// 拖拽模块
var modlder = el.modlder;
var lis = module.find('li'), elDrag = null;
// 左侧拖拽
module.delegate('li', {
dragstart: function(event) {
var li = $(this);
// 标记当前拖拽元素
elDrag = li;
event.originalEvent.dataTransfer.setData('text/plain', 'for firefox');
},
dragend: function(event) {
// 拖拽结束
elDrag = null;
event.preventDefault();
}
});
// 经过占位模块元素
modlder.on({
dragover: function(event) {
event.preventDefault();
},
dragenter: function() {
modlder.addClass(CL_DRAGENTER);
},
drop: function(event) {
var url = elDrag && elDrag.attr('data-url');
if (url) {
// 从左侧拖动到主内容区域时候放下的回调
// 在本例子中,就是插入大图
modlder.empty().hide();
// 在占位符元素的前面插入模块
self.insertModule('<img src="'+ url +'" height="'+ (elDrag.find('img').height() * 4) +'">', modlder, 'before');
}
modlder.removeClass(CL_DRAGENTER);
event.preventDefault();
}
});
// 模块响应左侧的拖拽以及其他模块的拖拽
var body = el.body;
// 实时记录鼠标的位置,方便判断鼠标当前在鼠标的上半区还是下半区
var pos = {};
document.addEventListener("dragover", function(event){
pos.y = event.pageY;
}, false);
body.delegate('.' + CL_MODULE, {
dragover: function(event) {
// 触发的节奏是:歘歘歘
var current = $(this);
// 1. 求得模块的水平中心位置
var centerY = current.offset().top + current.height() * 0.5;
if (typeof pos.y != 'number' || this == elDrag[0]) {
return;
}
if (!elDrag) {
return;
}
// 上半区还是下半区
if (pos.y <= centerY) {
// 上
// 如果拖拽的是li元素,认为是左侧列表
if (elDrag.is('li')) {
modlder.show().insertBefore(current);
} else if (elDrag.hasClass(CL_MODULE)) {
var prev = current.prev();
if (prev.length && prev.hasClass(CL_MODULE)) {
if (prev[0] !== elDrag[0]) {
elDrag.insertBefore(current);
}
} else {
elDrag.insertBefore(current);
}
}
} else {
if (elDrag.is('li')) {
modlder.show().insertAfter(current);
} else if (elDrag.hasClass(CL_MODULE)) {
// 当拖拽位置在下半区的时候
// 同时,拖拽元素不是检测元素的下一个的时候
// 执行位置调整
var next = current.next();
// 如果后面没元素,或者元素不是模块元素
if (next.length && next.hasClass(CL_MODULE)) {
if (next[0] !== elDrag[0]) {
// 如果后面的元素不是拖动的元素,位置变化
elDrag.insertAfter(current);
}
} else {
elDrag.insertAfter(current);
}
}
}
event.preventDefault();
},
dragenter: function(event) {
// 触发的节奏是: 歘 歘 歘
event.preventDefault();
},
// 模块间的拖来拖去
dragstart: function(event) {
elDrag = $(this).addClass(CL_DRAGSTART);
// 右侧删除
el.remove.addClass(CL_DRAGENTER);
event.originalEvent.dataTransfer.setData('text/plain', 'for firefox');
},
dragend: function(event) {
/*拖拽结束*/
$(this).removeClass(CL_DRAGSTART);
elDrag = null;
// 右侧删除
el.remove.removeClass(CL_DRAGENTER);
event.preventDefault();
}
});
// 拖动到删除元素时候
el.remove.on({
dragenter: function() {
$(this).addClass(CL_DRAGOVER);
},
dragover: function(event) {
event.preventDefault();
},
dragout: function() {
$(this).removeClass(CL_DRAGOVER);
},
drop: function(event) {
// 删除
if (elDrag && elDrag.hasClass(CL_MODULE)) {
var id = elDrag.attr('id');
if (id) {
// 删除该模块存储数据
delete self.store[id];
}
elDrag.remove();
if ($('.' + CL_MODULE).length == 0) {
modlder.show();
}
}
// 功成身退
$(this).removeClass(CL_DRAGENTER).removeClass(CL_DRAGOVER);
event.preventDefault();
}
});
},
insertModule: function(html) {
var self = this;
// 创建随机id
var id = ('id_' + Math.random()).replace('0.', '');
// 给模块HTML穿上一件可拖拽的外衣
var htmlWrap = '<div id="'+ id +'" class="'+ CL_MODULE +' module-drag" draggable="true">'
+ html
+ '</div>';
// 缓存颜色
var module = $(htmlWrap);
// 是否已经有过此模块
var isModuled = false;
$.each(self.store, function(id, obj) {
// 如果HTML内容一样,认为是同一模块
if (obj && obj.html && obj.html == html) {
isModuled = true;
return false;
}
});
if (isModuled && window.console) {
alert('此模块已经被选择');
return self;
}
// 存储
self.store[id] = {
html: html
};
// 占位符前面插入内容
self.el.modlder.before(module);
return self;
},
init: function() {
var self = this;
// 一些元素
self.el = $.extend(self.el, {
module: $('#module'),
remove: $('#moduleRemove'),
body: $('#moduleBody'),
stage: $('#moduleStage'),
modlder: $('#modulePlaHd')
});
self.events();
}
};
})();
moduleDrag.init();
</script>
</body>
</html>