发展
原生拖放的发展大致可分为三个阶段:IE4、IE5+、HTML5。
IE4 是最早在网页中引入 JavaScript 拖放功能的,当时只有图像和选中的文本可以拖放,并且只有文本框是有效的放置目标。
IE5 拖放功能进一步得到了扩展,添加了新的事件,几乎网页中的任何元素都可以作为放置目标。
IE5.5+ 又更进一步,让网页中的任何元素都可以拖放。
HTML5 在 IE 的实例基础上,进一步制定了拖放的规范。
Firefox 3.5+、Safari 3+、Chrome、Opera 12+也根据 HTML5 规范实现了原生拖放功能。
拖放事件
在进行拖放操作的不同阶段会触发数种事件。注意,在拖拽的时候只会触发拖拽的相关事件,鼠标事件,例如 mousemove,是不会触发的。当从操作系统拖拽文件到浏览器的时候,dragstart 和 dragend 事件不会触发。
拖动某元素时,依次触发以下事件
dragstart
当一个元素被拖拽的时候触发。
drag
触发 dragstart 后,随即会触发 drag 事件,而且在元素被拖动期间会持续触发该事件。
dragend
拖动停止时触发。(无论元素是放到了有效的放置目标还是无效的放置目标上)
元素拖放到有效的放置目标上时,依次触发以下事件
dragenter
元素被拖放到放置目标上触发。
dragover
被拖动的元素在放置目标范围内移动时,会持续触发该事件。
dragleave
元素被拖出了放置目标,触发该事件,并且 dragover 事件不再发生。
drop
元素被放到了放置目标中,触发该事件。
拖放步骤
定义拖拽目标
定义拖拽数据
(可选)设置拖动反馈图片
设置拖动效果
定义放置目标
拖放后的逻辑处理
定义拖拽目标
默认情况下,图像、链接和已选中的文本是可以拖动的。文本需要被选中才能拖动,而图像和链接在任何时候都可以拖动。
让其他元素可拖动也是可以的。HTML5 为所有 HTML 元素规定了一个 draggable
属性,表示是否可以拖动。图像和链接的 draggable 属性自动被设置成了 true,其他元素是 false。
// 设置图像不可拖动
<img src="http://www.seiue.com" draggable="false">
// 设置这个元素可以拖动
<div draggable="false">可拖动的DIV</div>
支持 draggable 属性的浏览器有 IE 10+、Firefox 4+、Safari 5+、Chrome 和 Opera 12+。
定义拖拽数据
我们有时会需要在拖放操作时进行数据交换,IE 引入了 dataTransfer
对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递数据。因为它是事件对象的属性,所以只能在拖放事件中访问,不能单独创建。目前,HTML5 规范也收入了 dataTransfer 对象。
实现数据交换主要用到两个方法:getData(type)
和setData(type, data)
。type
表示保存的数据类型,取值为“text”或“URL”,getData() 通过 setData() 设置的 type 来取到保存的值。
function dragStart(e) {
// 设置文本数据
e.dataTransfer.setData('text', 'hello world');
// 设置URL
e.dataTransfer.setData('URL', 'www.seiue.com');
}
function drop(e) {
// 接收文本数据
var text = e.dataTransfer.getData('text');
// 接收URL
var url = e.dataTransfer.getData('URL');
}
drag.addEventListener('dragStart', dragEnter, false);
drop.addEventListener('drop', drop, false);
建议总是添加 text/plain
和text/uri-list
来替代text
和URL
,因为后者存在兼容问题并且最终会被映射为前者。
设置拖动反馈图片
当发生一次拖拽时(dragstart 事件触发的元素),会在拖拽的元素处产生一个半透明的图片(该元素副本),并且会在拖拽过程中跟随鼠标移动,这就是反馈图片。这个图片是自动生成的,无需你亲自设定,但可以通过setDrawImage
来自定义一个反馈图片。
setDrawImage
是 dataTransfer 对象的一个方法。
event.dataTransfer.setDrawImage(image, xOffset, yOffset)
需要传入三个参数,分别是:一个图片的引用(可以是图片元素对象、canvas或其他元素对象),图像内的水平偏移量,图像内的垂直偏移量。下面的例子定义了一个反馈图片。
function dragCustomImage(e) {
var img = document.createElement('img')
img.setAttribute('src', 'drag-img.jpeg')
e.dataTransfer.setDragImage(img, 150, 150)
}
设置拖动效果
dataTransfer 对象不光能够传输数据,还能通过它来确定被拖动元素以及防止目标元素能够接收什么操作。主要用到两个属性:dropEffect
和effectAllowed
。
dropEffect
可以知道被拖动的元素能够执行哪种放置操作。这个属性有4个值。
none: 禁止把拖放的元素放在这里。
move: 可以把拖放的元素移动到放置目标。
copy: 可以把拖放的元素复制到放置目标。
link: 放置目标会打开拖动的元素(拖动的元素必须是一个链接,有URL)。
把元素拖放到放置目标时,以上的每个值都会导致光标显示不同的符号。每种操作触发什么样的行为需要你来自行实现,浏览器不会帮你移动、复制和打开链接。要使用 dropEffect,我们需要在 dragenter
事件处理程序中设置它的属性。
effectAllowed
dropEffect 属性需要搭配 effectAllowed 属性才生效。effectAllowed 属性表示允许拖动元素的哪种 dropEffect。effectAllowed 有以下值。
none: 被拖动的元素不能有任何行为。
copy: 只允许值为”copy“的 dropEffect。
link: 只允许值为“link” 的 dropEffect。
move: 只允许值为“move”的 dropEffect。
copyLink: 允许值为“copy”和“link”的 dropEffect。
copyMove: 允许值为“copy”和“move”的 dropEffect。
linkMove: 允许值为“link”和“move“的 dropEffect。
all: 允许任意 dropEffect。
需要在dragstart
事件处理程序中设置它的属性。
定义放置目标
所有元素都支持放置目标事件,但这些元素默认是不允许放置的。如果拖动的元素拖放到了不允许放置的元素,是不会触发drop
事件的。
把元素变成有效的放置目标,需要重写dragenter
和dragover
事件的默认行为。
var dropTarget = document.getElementById('dropTarget');
dropTarget.addEventListener('dragenter', function(e) {
e.preventDefault();
}, false);
dropTarget.addEventListener('dragover', function(e) {
e.preventDefault();
}, false);
处理拖放后的做的事情
在drop
事件发生时做一些事情,你可能想要获取拖拽目标携带的数据并做某些相应的事情。
参考资料
JavaScript高级程序设计(第3版)第16章 HTML5 脚本编程 – 原生拖放