html元素拖拽
二、基于HTML5拖拽API的拖拽
前序知识介绍
一个典型的拖拽操作是这样的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发(比如drag 和 dragover 事件类型)。
一个完整的drag and drop流程通常包含以下几个步骤:
- 设置可拖拽目标.设置属性
draggable="true"
实现元素的可拖拽. - 监听
dragstart
设置拖拽数据 - 为拖拽操作设置反馈图标(可选)
- 设置允许的拖放效果,如
copy
,move
,link
- 设置拖放目标,默认情况下浏览器阻止所有的拖放操作,所以需要监听
dragenter
或者dragover
取消浏览器默认行为使元素可拖放. - 监听
drop
事件执行所需操作
这里涉及几个知识点:
1、可拖动元素:又称为源对象,是指我们鼠标点击之后准备拖动的对象(图片、div、文字等)
2、可放置元素:又称为目标对象,是指可以放置源对象的区域
3、事件:
Event | On Event Handler | Description |
---|---|---|
drag | ondrag | 当拖动元素或选中的文本时触发 |
dragend | ondragend | 当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键) |
dragenter | ondragenter | 当拖动元素或选中的文本到一个可释放目标时触发 |
dragexit | ondragexit | 当元素变得不再是拖动操作的选中目标时触发 |
dragleave | ondragleave | 当拖动元素或选中的文本离开一个可释放目标时触发 |
dragover | ondragover | 当元素或选中的文本被拖到一个可释放目标上时触发 |
dragstart | ondragstart | 当用户开始拖动一个元素或选中的文本时触发 |
drop | ondrop | 当元素或选中的文本在可释放目标上被释放时触发 |
ps:当从操作系统向浏览器中拖动文件时,不会触发dragstart 和dragend 事件
4、接口:
HTML5为所有的拖动相关事件提供了一个新的属性:
event.DataTransfer对象
属性和方法 |
描述 |
dropEffect | 拖拽交互类型,通常决定浏览器如何显示鼠标光标并控制拖放操作.常见的取值有copy ,move ,link 和none |
effectAllowed | 指定允许的交互类型,可以取值:copy ,move ,link ,copyLink ,copyMove ,limkMove , all , none 默认为uninitialized (允许所有操作) |
files | 包含File 对象的FileList 对象.从操作系统向浏览器拖放文件时有用. |
types | 保存DataTransfer 对象中设置的所有数据类型. |
setData(format, data) | 以键值对设置数据,format通常为数据格式,如text ,text/html |
getData(format) | 获取设置的对应格式数据,format与setData()中一致 |
clearData(format) | 清除指定格式的数据 |
setDragImage(imgElement, x, y) | 设置自定义图标 |
源对象和目标对象的事件间传递数据
event.dataTransfer {}//数据传递对象
源对象上的事件处理中保存数据:
event.dataTransfer.setData(key,value);//key,value必须都是字符串类型
如:event.dataTransfer.setData("text/plain", "This is text to drag");
目标对象上的事件处理中读取数据:
var value2 = event.dataTransfer.getData(key);
demo
<div id="demo1"> <ul class="panel-list"> <li class="panel-item"></li> <li class="panel-item"></li> <li class="panel-item"></li> <li class="panel-item"></li> <li class="panel-item"></li> </ul> <h2>拖拽下面的方块到上面任意容器中</h2> <!-- 设置draggable使元素成为可拖拽元素 --> <div class="movable" id="demo1-src" draggable="true" draggable="true"> <img style="width:100%;" src="__ADMIN__/img/pbl/11.jpg"> </div> <style> #demo1 { margin: 20px; } #demo1 .panel-list { overflow: hidden; list-style: none; margin: 0; padding: 0; } #demo1 .panel-item { float: left; margin-right: 30px; width: 100px; height: 100px; background: #ddd; border: 1px solid #ddd; } #demo1-src { display: inline-block; width: 50px; height: 50px; background: purple; } #demo1 .over { border: 1px dashed #000; -webkit-transform: scale(0.8, 0.8); } </style> <script> (function () { var dnd = { // 初始化 init: function () { var me = this; me.src = document.querySelector('#demo1-src');//获取指定css选择器的元素 me.panelList = document.querySelector('.panel-list');//获取指定css选择器的元素 console.log( me.panelList ); //addEventListener('要监听的事件名','事件触发的函数',布尔:指定事件是否在捕获或冒泡阶段执行) // 为拖拽源监听dragstart,设置关联数据 me.src.addEventListener('dragstart', me.onDragStart, false); // 拖拽鼠标移入元素,在拖放目标上设置视觉反馈 me.panelList.addEventListener('dragenter', me.onDragEnter, false); // 取消元素dragover默认行为,使其可拖放 me.panelList.addEventListener('dragover', me.onDragOver, false); // 拖拽移出元素,清除视觉反馈 me.panelList.addEventListener('dragleave', me.onDragLeave, false); // 鼠标释放,在拖放目标上接收数据并处理 me.panelList.addEventListener('drop', me.onDrop, false); }, onDragStart: function (e) { //setData(format, data) 以键值对设置数据,format通常为数据格式,如text,text/html //设置可拖动元素的id(存起来) e.dataTransfer.setData('text/plain', 'demo1-src'); e.target.style.height="100px"; e.target.style.cursor="move"; }, //当拖动元素或选中的文本到一个可释放目标时触发 onDragEnter: function (e) { console.log(e.target); //e.target : <ul class="panel-list"></ul> //panel-list元素的子元素的类名列表中包含panel-item类则添加over类(呈现虚框效果) if (e.target.classList.contains('panel-item')) { //为panel-item的元素添加over类 e.target.classList.add('over'); } }, //当拖动元素或选中的文本离开一个可释放目标时触发(移除over类) onDragLeave: function (e) { e.target.style.cursor="move"; if (e.target.classList.contains('panel-item')) { e.target.classList.remove('over'); } }, //当元素或选中的文本被拖到一个可释放目标上时触发(取消事件的默认动作比如说表单自动提交) onDragOver: function (e) { e.preventDefault(); }, //当元素或选中的文本在可释放目标上被释放时触发 onDrop: function (e) { var id = e.dataTransfer.getData('text/plain'); var src = document.getElementById(id); var target = e.target; if (target.classList.contains('panel-item')) { target.appendChild(src); target.classList.remove('over'); } } }; dnd.init(); }()); </script> </div> <div id="demo2"> <h3>从文件夹中拖拽图片到下面的区域进行预览</h3> <ul class="preview"></ul> <style> #demo2 { margin: 20px; } #demo2 .preview { height: 300px; background: #ddd; } #demo2 li { float: left; margin-left: 40px; } #demo2 img { max-height: 150px; width: auto; } </style> <script> (function (w) { var doc = w.document; var dnd = { init: function () { var me = this; var preview = doc.querySelector('#demo2 .preview'); preview.addEventListener('dragover', function (e) { e.preventDefault(); }, false); preview.addEventListener('drop', function (e) { // 操作系统拖放文件到浏览器需要取消默认行为 e.preventDefault(); [].forEach.call(e.dataTransfer.files, function (file) { if (file && file.type.match('image.*')) { var reader = new FileReader(); reader.onload = function (e) { var img = doc.createElement('img'); img.src = e.target.result; var li = doc.createElement('li'); li.appendChild(img); preview.appendChild(li); }; reader.readAsDataURL(file); } }); }, false); } }; dnd.init(); }(window)); </script> </div> <!-- demo2 -->