JavaScript原生拖放API入门总结
一.背景
最早实现JavaScript拖放功能的是IE4的浏览器了.在当时,网页中只有图像和文本才能够进行拖放.IE5之后,拖放功能得到了扩展,形成了一个API(应用程序编程接口),使得几乎任何的标签元素都可以实现拖放.
HTML5为拖放制定了规范,firefox3.5、safari3+和chrome也根据这个规范实现了拖放功能.一说起拖放,几乎在任何应用框架窗口内,都可以拖放网页元素,浏览器对拖放功能的支持也提供了便利.
二.拖放事件
通过拖放事件,可以控制拖放的各方面.首先要确定哪里发生了拖放事件,具体点说就是哪个元素是被拖动,哪个元素是被作为放置目标.拖动某个元素,将依次触发:
(1)dragstart
(2)drag
(3)dragend
这三个事件.当用户按下鼠标键并开始移动鼠标时,被拖动的某个元素就会触发dragstart事件,也就是拖动开始事件,通过这个事件可以执行相关的JavaScript代码.此时鼠标光标变成一种不能放的符号(圆环中有一条反斜线),表示不能把元素放到自己上面.
触发dragstart事件之后,随即触发drag事件,即拖动事件,并且在元素被拖动期间会持续触发该事件.当拖动停止(无论把被拖动的元素放在有效的放置目标上还是无效的放置目标上),都会触发dragend事件,即拖动结束事件.
在上述的三个事件中,被拖动的元素始终是该三个事件的目标.默认情况下,被拖动元素的外观是不会改变的,但是可以自己修改.大多数浏览器会为被拖动元素创建一个半透明的副本,并且这个副本始终跟随鼠标光标移动.
当被拖动元素被拖动到有效目标上时,下列事件会依次发生:
(1).dragenter
(2).dragover
(3).dragleave或drop
只要有元素被拖动到放置目标上,就会触发dragenter事件,即拖动进入目标元素事件.紧随其后是dragover事件,而且被拖动元素还持续在放置目标范围内移动时,还会持续触发dragover事件,即拖动结束事件.如果被拖动元素离开了放置目标,则不会触发dragover事件,而是触发dragleave事件,即拖动离开事件.当被拖动元素处于放置目标中,则触发drop事件,而不是dragleave事件,即拖动停止事件.这三个事件的作用目标都是作为放置目标的元素.
尽管所有元素都可以作为被放置的目标,但是这些元素默认是不允许放置的.如果被拖动元素经过不允许放置的元素,无论用户如何操作,也不会触发drop事件.而要想让元素成为有效的被放置的目标,就是重写dragenter和dragover事件的默认行为.比如我们用如下代码可以将一个元素变成有效的放置目标.
如上图所示,定义了一个id为dragTarget的div元素,当这个元素作为放置目标,就会触发dragenter事件和dragover事件,这时,我们只需要preventDefault()这个方法就可以阻止默认事件行为,就可以将这个div元素作为被放置目标.document.getElementById()这个方法就是找到dom元素(DOM中会讲到).至于dragenter事件中再加一个阻止默认行为就可以了.代码如下:
//重写拖动进入事件
dt.ondragenter = function(event){
event.preventDefault();
}
这样就成功将一个div元素作为被放置的目标呢,然后只要你将被拖动元素拖动到该元素范围内,你会发现光标变成允许放置的符号了.
在Firefox3.5中,放置事件的默认行为是打开被放置目标的URL.换句话说,如果把图像拖动到放置目标,页面会像打开一个超链接那样转向图像文件,而如果是把文本拖放到放置目标上,则会导致无效的URL错误.因此为了保证兼容性,也就是Firefox3.5支持正常的拖放,还要取消drop事件的默认行为,阻止它打开URL.代码如下:
只有简单的拖放而没有数据的变化是没有什么用的,为了在拖放操作中实现数据交换,IE5引入了dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据.因为它是事件对象的属性,因此只能在拖放事件中访问这个对象.利用这个对象的属性和方法可以完善拖放功能.
dataTransfer对象主要有两个方法:getData()与setData().顾名思义,setData()方法就是保存数据,可以接收两个参数,第一个参数表示存储的数据类型,通常是取值为"text"和"url".这个参数也是getData()方法中的唯一一个参数,即取得保存的字符串值.第二个参数则是你要保存的数据,可以是元素的id属性,class属性等,也可以是一段自定义的文本等.示例代码如下:
如上图所示,我又在页面中定义了一个p元素,表示被拖动的元素,当拖动开始事件即dragstart事件触发时,我就存储这个元素中包含的文本,即"被拖动的元素"这段文本.然后我们就在drop事件触发中获取这个存储的值.代码如下:
IE5只定义了"text"和"url"这两个数据类型,但HTML5对此进行了扩展,可以是任意的MIME类型.考虑兼容性,HTML5当然也支持"text"和"url",但这两种数据类型会被映射为"text/plain"和"text/uri-list".
保存在这个对象中的数据只能在drop事件中读取到,如果没有读取到,则说明这个对象已经被销毁了.dataTransfer对象实际上可以为每种MIME类型保存一个值,换句话说,就是可以同时保存一段文本和一个url.
当拖动文本时,浏览器就会调用setData()方法,以"text"格式将文本保存在dataTransfer对象中.类似的,拖放链接和图像时,则保存url.然后当被拖动元素拖动到放置目标时,就可以通过getData()方法读取到保存的值.
将数据作为"text"和作为"url"保存是有区别的.如果只是"text"格式,那么得到的数据则不会做任何特殊处理.但如果是"url"格式,浏览器会将这个得到的值当作一个链接.换句话说你把它放到另一个浏览器当中,浏览器会打开该url.
Firefox在其第五个版本之前是不能正确的将"url"和"text"映射成"text/uri-list"和"text/plain"的.但却能把"Text"映射成"text/plain".因此,为了保证兼容性,在调用getData()方法时取url类型的数据最好检测两个值,而在取text类型的数据则使用"Text".代码如下:
//使用逻辑与操作符检测两个值,注意短数据类型一定要放在前面,因为IE10及之前的版本是不支持扩展的MIME类型名的.
var data = event.dataTransfer.getData("url") || event.dataTransfer.getData("text/uri-list");
var text = event.dataTransfer.getData("Text");
利用dataTransfer对象不仅能交换数据,还能够通过它来确定被拖动元素或者放置目标能够执行什么操作.这就需要访问这个对象的两个属性:dropEffect和effectAllowed.
可以从dropEffect属性知道被拖动的元素能够执行哪种放置行为.这个属性有4个可能的值.
"none":不能把拖动的元素放置到这里.这是除了文本框以外所有元素的默认值.
"move":应该把拖动的元素移动到该放置目标.
"copy":应该把拖动的元素复制到该放置目标.
"link":表示放置目标会打开拖动的元素.(但拖动的元素必须是一个链接,有url).
把元素拖动到放置目标上时,以上的每一个值都会导致光标显示为不同的符号,这是浏览器的默认行为.然而浏览器只能改变光标的样式,光标所指示的动作还需要开发人员自己决定.要使用dropEffect属性,必须在dragenter事件中针对放置目标设置.
dropEffect属性只有搭配effectAllowed属性才有用.effectAllowed属性表示允许拖动元素以那种dropEffect方式拖动.该属性的可能有的值如下.
"uninitialized":没有给被拖动元素设置任何放置行为.
"none":被拖动元素不能有任何行为.
"copy":只允许值为"copy"的dropEffect.
"link":只允许值为"link"的dropEffect.
"move":只允许值为"move"的dropEffect.
"copyMove":允许值为"copy"和"move"的dropEffect.
"copyLink":允许值为"copy"和"link"的dropEffect.
"linkMove":允许值为"link"和"move"的dropEffect.
"all":允许任意的dropEffect.
必须在dragstart事件中设置effectAllowed属性.
假设你想允许用户将一个文本框中的文本拖放到一个div元素中,首先就必须要将dropEffect和effectAllowed设置为"move".然后重写放置事件的默认行为,就能从文本框中移走文本.然后就可以自己编写代码将文本插入到div元素中,这样就完成了整个拖放操作,当然只是拖放一个元素则不必如此做.如果只是设置为"copy"值,则会复制该文本到div元素中.
注:Firefox5及之前的版本中在处理effectAllowed属性时有一个问题,即如果你设置了这个属性的值,不一定会触发drop事件.
默认情况下,图片,链接和文本是可以拖动的,也就是说,不用额外的编写代码,用户就可以拖动它.文本只有在选中的情况下才能被拖动,而图像和链接则在任何时候都可以拖动.
让其他元素拖动也是可以的.HTML5为所有的html属性定义了一个draggable属性,将这个属性设为布尔值true或false,就表示是否可以拖动该元素.图像和链接的这个属性是自动被设置成true的.当然也可以手动将其设为false.如下:
//让这个图片不能被拖动
<img src="xxx.png" draggable="false">
//让这个元素可以被拖动
<div class="xxx" draggable="true">
注:在IE9及更早的版本中,通过mousedown事件调用dragDrop()方法能够让任何元素可以拖动.而在safari4及之前的版本中,必须额外给元素设置CSS样式:-khtml-user-drag:element.
支持draggable属性的浏览器有IE10+,Firefox4+,safari5+和chrome.opera11.5及之前的版本均不支持HTML5的拖放功能.另外为了让Firefox支持可拖动属性,还必须添加一个ondragstart事件,并在dataTransfer对象中保存一些信息.
HTML5规范还规定了dataTransfer对象应该包含下列属性和方法:
1.addElement(element):为拖动操作添加一个元素,添加这个元素只影响数据(即增加作为拖动源所响应回调的对象),并不会影响拖动操作时页面元素的外观.目前似乎只有Firefox3.5+实现了这个方法.
2.clearData(format):清除以特定格式保存的数据.支持该方法的浏览器有 IE,Firefox3.5+,chrome和safari4+.
3.setDragImage(element,x,y):指定一幅图像,当拖动发生时,显示在光标下方.三个参数分别是要显示的HTML元素和光标在图像中的x,y坐标.其中HTML元素可以是一幅图像,也可以是一个其它元素,是图像则显示图像,是其它元素则显示渲染后的元素.支持该方法的浏览器有firefox3.5+,safari4+和chrome.
4.types:当前保存的数据类型.这是一个类似数组的集合(注意不是数组),以"text"这样的字符串格式保存的数据类型.支持该属性的浏览器有IE10+,Firefox3.5+和chrome.
以下为示例:
拖动元素
若要查看效果,请自行前往该网址查看:
http://eveningwater.com/test/jsTest/dragdemo.html
2.随意拖动元素,自行前往该网址查看代码与效果:
http://eveningwater.com/test/jsTest/drag.html
3.一个限制拖放范围的示例.
首先确定思路,要有一个被拖动的元素,与限定拖动范围内的放置目标.被拖动的元素我们可以通过定位来进行移动,定位就需要四个属性,即left,top,bottom与right.这里只考虑left与top即可.既然要给定范围内进行拖动,那么我们就需要知道这个作为限定拖动范围的放置目标的宽度和高度,以这个宽度和高度作为比较,当被拖动元素的left值大于了宽度,我们就让left等于该宽度,也就是临界值.同样的道理,高度也是如此.而当left小于0时,我们就可以让left等于0.然后事件就是鼠标按下时的事件,鼠标移动中的事件与鼠标光标停止移动的事件,即mousedown,mouseup,mousemove事件.鼠标按下事件是作用于被拖动元素的,而后两者则是作用于整个文档,因此就是document.onmouseup与document.onmousemove.
我们先写HTML代码,给定两个元素,第一个是被拖动元素,第二个是放置目标,然后分别给这两个元素设置一个唯一的id属性,代码如下:
然后,我们给两个元素分别设置宽高,为了便于区分,我给放置目标元素设置边框,被拖动元素设置背景颜色,代码如下:
然后,我们可以查看效果,如下图所示:
现在开始写js代码,思路也说清楚了,直接上代码:
具体效果可前往这个网址查看:http://eveningwater.com/test/jsTest/dragLimit.html
JavaScript原生拖放API入门总结的更多相关文章
- 深入理解javascript原生拖放
× 目录 [1]拖放源 [2]拖放目标 [3]dataTransfer对象[4]改变光标 前面的话 拖放(drag-and-drop,DnD)其实是两个动作——拖和放.所以,它涉及到两个元素.一个是被 ...
- javascript 原生常用api 数组方法大全
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- jQuery? 回归JavaScript原生API
如今技术日新月异,各类框架库也是层次不穷.即便当年漫山红遍的JQuery(让开发者write less, do more,So Perfect!!)如今也有被替代的大势.但JS原生API写法依旧:并且 ...
- 《JAVASCRIPT高级程序设计》原生拖放和媒体元素
一.原生拖放 最早在网页中引入javascript拖放功能的是IE4,当时,网页中只有两种对象可以拖放:图像和某些文本.而现在,几乎网页中的任何元素都可以拖放以及作为放置目标.下面介绍一些与拖放相关的 ...
- 转载-Web API 入门
An Introduction to ASP.NET Web API 目前感觉最好的Web API入门教程 HTTP状态码 Web API 强势入门指南 Install Mongodb Getting ...
- HTML5原生拖放实例分析
HTML5提供了原生拖放功能的JavaScript API,使用起来很方便. 兼容性: 对于PC端浏览器,Firefox.Chrome.Safari支持良好,而IE和Edge浏览器有些特性不支持,如I ...
- JavaScript原生Array常用方法
JavaScript原生Array常用方法 在入门Vue时, 列表渲染一节中提到数组的变异方法, 其中包括push(), pop(), shift(), unshift(), splice(), so ...
- 我拖拖拖--H5拖放API基础篇
不要搞错,本文不是讲如何拖地的.看过<javascript精粹>朋友应该知道,他实现拖放的过程比较复杂,现在时代不同了,我们用H5的新的拖放API就能非常方便的实现拖放效果了.最近在园子见 ...
- jQuery 事件绑定 和 JavaScript 原生事件绑定
总结一下:jQuery 事件绑定 和 JavaScript 原生事件绑定 及 区别 jQuery 事件绑定 jQuery 中提供了四种事件监听绑定方式,分别是 bind.live.delegate.o ...
随机推荐
- Struts2是什么?
Struts2是什么: Struts2是整合了struts1和webwork的技术优点的使用广泛的MVC框架: Struts2的特点: 1.基于MVC框架,结构清晰,便于开发人员掌控开发流程: 2.使 ...
- Jetson TX2安装固态硬盘(原创)
SSD on Jetson TX2 注意事项:在断电情况下,将固态硬盘的接线与Jetson TX2进行连接 步骤: 一.jetson tx2开机,打开搜索栏中的Disks 二.Disks显示画面 三. ...
- linux socket 编程(C语言)[转]
最近看了一些网络编程的书籍,一直以来总感觉网络编程神秘莫测,其实网络编程入门还是很容易学的,下面这些代码是我在linux下编写的,已经运行过了,编译之后就可以运行了.有不足之处希望大家多多指出,共同学 ...
- github上最好的开源MMORPG - stendhal
Stendhal is a fully fledged multiplayer online adventures game (MORPG). It is completely open source ...
- NIO基础篇(二)
Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件.这样,一个单独的线程可以管理多个channel,从而管理多个网络连接. 传统的 ...
- Spring整合JMS(二)——三种消息监听器
原文地址:http://haohaoxuexi.iteye.com/blog/1893676 1.3 消息监听器MessageListener 在Spring整合JMS的应用中我们在定义消息监 ...
- webrtc初探之一对一的连接过程(一)
说明,我研究的是muan-khan的一个github项目,针对的是chrome对chrome,也就是pc对pc的一对一,一对多通话,感兴趣的可以继续往下看. github地址:https://gith ...
- BZOJ 3512: DZY Loves Math IV [杜教筛]
3512: DZY Loves Math IV 题意:求\(\sum_{i=1}^n \sum_{j=1}^m \varphi(ij)\),\(n \le 10^5, m \le 10^9\) n较小 ...
- [Sdoi2017]新生舞会 [01分数规划 二分图最大权匹配]
[Sdoi2017]新生舞会 题意:沙茶01分数规划 貌似\(*10^7\)变成整数更科学 #include <iostream> #include <cstdio> #inc ...
- Linux 下编写服务器程序时关于Address already in use 的小错误
新手,,学习linux服务器编程的时候,bind()函数出现了Address already in use 的错误,这是因为上一次bind过后,还未释放,,只要在socket和bind之间加一个函数就 ...