谈谈对JavaScript原生拖放的深入理解


Posted in Javascript onSeptember 20, 2016

前面的话

拖放(drag-and-drop,DnD)其实是两个动作——拖和放。所以,它涉及到两个元素。一个是被拖的元素,称为拖放源;另一个是要放的目标,称为拖放目标。本文将通过拆分这两个概念来详细介绍原生拖放

拖放源

什么样的元素才是拖放源呢?

HTML5为所有HTML元素规定了一个draggable属性,表示元素是否可以拖动

图像和链接的draggable属性自动被设置成了true,而其他元素这个属性的默认值都是false

[注意]必须设置draggable='true'才能生效,只设置draggable不起作用

默认情况下,文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。而其他元素则无法被拖放

<input value="文字可拖动">
<img alt="图像可拖动" src="http://files.cnblogs.com/files/xiaohuochai/zan.gif">
<a href="#">链接可拖动</a>
<div id="test" style="height:30px;width:300px;background:pink;">元素不可拖动</div>

当为元素设置draggable属性后,普通元素也可以拖动

<div draggable="true" style="height:30px;width:100px;background:pink;"></div>

兼容

IE9-浏览器不支持draggable属性,但可通过mousedown事件处理程序调用dragDrop()方法来实现拖动效果

<div id="test" style="height:30px;width:300px;background:pink;"></div> 
<script>
test.onmousedown = function(){
this.dragDrop();
}
</script>

[注意]如果让firefox支持draggable属性,必须添加一个ondragstart事件处理程序,并在dataTransfer对象使用setData()方法来启动效果

拖放事件

拖放源涉及到3个拖放事件。拖动拖放源时,依次触发dragstart、drag和dragend这3个事件

dragstart

按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发dragstart事件。此时光标变成“不能放”符号(圆环中有一条反斜线),表示不能把元素放到自己上面

drag

触发dragstart事件后,随即会触发drag事件,而且在元素被拖动期间会持续触发该事件

dragend

当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发dragend事件

<div id="test" draggable="true" style="height:30px;width:100px;background:pink;">0</div> 
<script>
var timer,i=0;
test.ondragstart = function(){
this.style.backgroundColor = 'lightgreen';
}
test.ondrag = function(){
if(timer) return;
timer = setInterval(function(){
test.innerHTML = i++;
},100)
}
test.ondragend = function(){
clearInterval(timer);
timer = 0;
this.style.backgroundColor = 'pink';
}
</script>

拖放目标

拖放目标是指被拖动的元素松开鼠标时被放置的目标

拖放源被拖动到拖放目标上时,将依次触发dragenter、dragover和dragleave或drop这四个事件

dragenter

只要有元素被拖动到放置目标上,触发dragenter事件

dragover

被拖动的元素在放置目标的范围内移动时,持续触发dragover事件

dragleave

如果元素被拖出了放置目标,触发dragleave事件

drop

如果元素被放到了放置目标中,触发drop事件

[注意]firefox浏览器的drop事件的默认行为是打开被放到放置目标上的URL。为了让firefox支持正常的拖放,还要取消drop事件的默认行为

默认情况下,目标元素是不允许被放置的,所以不会发生drop事件。只要在dragover和dragenter事件中阻止默认行为,才能成为被允许的放置目标,才能允许发生drop事件。此时,光标变成了允许放置的符号

<div id="test" draggable="true" style="height:30px;width:130px;background:pink;float:left;">拖放源</div> 
<div id="target" style="float:right;height: 200px;width:200px;background:lightblue;">拖放目标</div>
<script>
var timer,i=0;
var timer1,i1=0;
//兼容IE8-浏览器
test.onmousedown = function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(){
this.style.backgroundColor = 'lightgreen';
this.innerHTML = '开始拖动';
}
test.ondrag = function(){
if(timer) return;
timer = setInterval(function(){
test.innerHTML = '元素已被拖动' + ++i + '秒';
},1000);
}
test.ondragend = function(){
clearInterval(timer);
timer = 0;i =0;
this.innerHTML = '结束拖动';
this.style.backgroundColor = 'pink';
}
target.ondragenter = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.innerHTML = '有元素进入目标区域';
this.style.background = 'red';
}
target.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
if(timer1) return;
timer1 = setInterval(function(){
target.innerHTML = '元素已进入' + (++i1) + '秒';
},1000);
}
target.ondragleave = function(){
clearInterval(timer1);
timer1 = 0;i1=0;
this.innerHTML = '元素已离开目标区域';
this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(){
clearInterval(timer1);
timer1 = 0;i1=0;
this.innerHTML = '元素已落在目标区域';
this.style.backgroundColor = 'orange'; 
}
</script>

dataTransfer对象

为了在拖放操作时实现数据交换,引入了dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据

dataTransfer对象有两个主要方法:getData()和setData()

getData()可以取得由setData()保存的值。setData()方法的第一个参数,也是getData()方法唯一的一个参数,是一个字符串,表示保存的数据类型,取值为"text"或"URL"

IE只定义了"text"和"URL"两种有效的数据类型,而HTML5则对此加以扩展,允许指定各种MIME类型。考虑到向后兼容,HTML5也支持"text"和"URL",但这两种类型会被映射为"text/plain"和"text/uri-list"

实际上,dataTransfer对象可以为每种MIME类型都保存一个值。换句话说,同时在这个对象中保存一段文本和一个URL不会有任何问题

[注意]保存在dataTransfer对象中的数据只能在drop事件处理程序中读取

在拖动文本框中的文本时,浏览器会调用setData()方法,将拖动的文本以"text"格式保存在dataTransfer对象中。类似地,在拖放链接或图像时,会调用setData()方法并保存URL。然后,在这些元素被拖放到放置目标时,就可以通过getData()读到这些数据

<div>请将从这堆内容不同乱七八糟的文字中挑选一些移动到拖放目标中</div> 
<div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目标</div>
<div id="result"></div>
<script>
target.ondragenter = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.innerHTML = '有元素进入目标区域';
this.style.background = 'red';
}
target.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
target.ondragleave = function(e){
e = e || event; 
this.innerHTML = '元素已离开目标区域';
this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
result.innerHTML = '落入目标区域的文字为:' + e.dataTransfer.getData('text');
this.innerHTML = '元素已落在目标区域';
this.style.backgroundColor = 'orange'; 
}
</script>

当然,也可以在dragstart事件处理程序中调用setData(),手动保存自己要传输的数据,以便将来使用

<div id="test" draggable="true" data-value="这是一个秘密" style="height:30px;width:100px;background:pink;">拖动源</div> 
<div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目标</div>
<div id="result"></div>
<script>
//兼容IE8-浏览器
test.onmousedown = function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(e){
e = e || event;
e.dataTransfer.setData('text',test.getAttribute('data-value'));
}
target.ondragenter = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.innerHTML = '有元素进入目标区域';
this.style.background = 'red';
}
target.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
}
target.ondragleave = function(e){
e = e || event; 
this.innerHTML = '元素已离开目标区域';
this.style.backgroundColor = 'lightblue';
}
target.ondrop = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
result.innerHTML = '落入目标区域的文字为:' + e.dataTransfer.getData('text');
this.innerHTML = '元素已落在目标区域';
this.style.backgroundColor = 'orange'; 
}
</script>

改变光标

利用dataTransfer对象,不仅可以传输数据,还能通过它来确定被拖动的元素以及作为放?目标的元素能够接收什么操作。为此,需要访问dataTransfer对象的两个属性:dropEffect和effectAllowed

实际上,这两个属性并没有什么用,只是拖动源在拖动目标上移动时,改变不同的光标而已(但是,有一种情况除外)

dropEffect

dropEffect属性可以知道被拖动的元素能够执行哪种放置行为。这个属性有下列4个可能的值

"none":不能把拖动的元素放在这里。这是除文本框之外所有元素的默认值(此时,将无法触发drop事件)

"move":应该把拖动的元素移动到放置目标

"copy":应该把拖动的元素复制到放置目标

"link":表示放置目标会打开拖动的元素(但拖动的元素必须是一个链接,有URL)

在把元素拖动到放置目标上时,以上每一个值都会导致光标显示为不同的符号

[注意]必须在ondragover事件处理程序中针对放置目标来设置dropEffect属性

effectAllowed

dropEffect属性只有搭配effectAllowed属性才有用。effectAllowed属性表示允许拖动元素的哪种dropEffect

effectAllowed属性可能的值如下

"uninitialized":没有给被拖动的元素设置任何放置行为

"none":被拖动的元素不能有任何行为 

 "copy":只允许值为"copy"的dropEffect

"link"只允许值为"link"的dropEffect

"move":只允许值为"move"的dropEffect

"copyLink":允许值为"copy"和"link"的dropEffect

"copyMove":允许值为"copy"和"move"的dropEffect

"linkMove":允许值为"link"和"move"的dropEffect

 "all":允许任意dropEffect

[注意]必须在ondragstart事件处理程序中设置effectAllowed属性

<div id="test" draggable="true" style="height:30px;width:100px;background:pink;display:inline-block;">拖放源</div>
<br>
<div id="target1" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(none)拖放目标</div>
<div id="target2" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(move)拖放目标</div>
<div id="target3" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(copy)拖放目标</div>
<div id="target4" style="margin-top:20px;height: 100px;width:150px;background:lightblue;display:inline-block;">(link)拖放目标</div>
<div id="result"></div>
<script>
//兼容IE8-浏览器
test.onmousedown =function(){
if(this.dragDrop){
this.dragDrop();
}
}
test.ondragstart = function(e){
e = e || event;
//兼容firefox浏览器
e.dataTransfer.setData('text','');
e.dataTransfer.effectAllowed = 'all';
}
target1.ondragenter = target2.ondragenter =target3.ondragenter =target4.ondragenter =function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}this.style.background = 'red';
}
target1.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'none';
}
target2.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'move';
}
target3.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'copy';
}
target4.ondragover = function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
e.dataTransfer.dropEffect = 'link';
}
target1.ondragleave = target2.ondragleave =target3.ondragleave =target4.ondragleave =function(e){
e = e || event; this.style.backgroundColor = 'lightblue';
}
target1.ondrop = target2.ondrop =target3.ondrop =target4.ondrop =function(e){
e = e || event;
if(e.preventDefault){
e.preventDefault();
}else{
e.returnValue = false;
}
this.style.backgroundColor = 'orange'; 
}
</script>

以上所述是小编给大家介绍的JavaScript原生拖放,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
用innerhtml提高页面打开速度的方法
Aug 02 Javascript
jQuery选择器之基本选择器与层次选择器
Mar 03 Javascript
深入分析JSON编码格式提交表单数据
Jun 25 Javascript
jQuery悬停文字提示框插件jquery.tooltipster.js用法示例【附demo源码下载】
Jul 19 Javascript
微信小程序 实战实例开发流程详细介绍
Jan 05 Javascript
javascript判断回文数详解及实现代码
Feb 03 Javascript
jquery获取下拉框中的循环值
Feb 08 Javascript
JS实现的数字格式化功能示例
Feb 10 Javascript
jQuery实现分页功能(含ajax请求、后台数据、附完整demo)
Apr 03 jQuery
简单实现js放大镜效果
Jul 24 Javascript
简单了解TypeScript中如何继承 Error 类
Jun 21 Javascript
关于uniApp editor微信滑动问题
Jan 15 Javascript
Javascript获取图片原始宽度和高度的方法详解
Sep 20 #Javascript
AngularJS 过滤器(自带和自建)详解
Sep 19 #Javascript
js Canvas实现圆形时钟教程
Sep 19 #Javascript
Bootstrap模态框调用功能实现方法
Sep 19 #Javascript
javascript实现的上下无缝滚动效果
Sep 19 #Javascript
Angular ng-class详解及实例代码
Sep 19 #Javascript
javascript实现的左右无缝滚动效果
Sep 19 #Javascript
You might like
php获得文件扩展名三法
2006/11/25 PHP
PHP HTML JavaScript MySQL代码如何互相传值的方法分享
2012/09/30 PHP
php include类文件超时问题处理
2015/02/06 PHP
PHP生成制作验证码的简单实例
2016/06/12 PHP
onsubmit阻止form表单提交与onclick的相关操作
2010/09/03 Javascript
jquery分页插件jpaginate在IE中不兼容问题
2014/04/22 Javascript
jquery实现在页面加载完毕后获取图片高度或宽度
2014/06/16 Javascript
理解JavaScript事件对象
2016/01/25 Javascript
ES6生成器用法实例分析
2017/04/10 Javascript
jQuery中ajax获取数据赋值给页面的实例
2017/12/31 jQuery
Node在Controller层进行数据校验的过程详解
2020/08/28 Javascript
Node.js利用Express实现用户注册登陆功能(推荐)
2020/10/26 Javascript
[43:18]NB vs Infamous 2019国际邀请赛淘汰赛 败者组 BO3 第一场 8.22
2019/09/05 DOTA
[00:49]完美世界DOTA2联赛10月28日开团时刻:随便打
2020/10/29 DOTA
python学习之编写查询ip程序
2016/02/27 Python
Python中的条件判断语句与循环语句用法小结
2016/03/21 Python
Python简单实现Base64编码和解码的方法
2017/04/29 Python
Python多进程池 multiprocessing Pool用法示例
2018/09/07 Python
django 外键model的互相读取方法
2018/12/15 Python
详解python中自定义超时异常的几种方法
2019/07/29 Python
Django项目中实现使用qq第三方登录功能
2019/08/13 Python
Python如何实现后端自定义认证并实现多条件登陆
2020/06/22 Python
pytorch学习教程之自定义数据集
2020/11/10 Python
CSS3 选择器 基本选择器介绍
2012/01/21 HTML / CSS
美国运动鞋类和服装零售连锁店:Shoe Palace
2019/08/13 全球购物
戴森香港官方网站:Dyson香港
2021/02/11 全球购物
酒店营销策划方案
2014/02/07 职场文书
党员党性分析材料
2014/02/17 职场文书
致共产党员倡议书
2014/04/16 职场文书
关于美容院的活动方案
2014/08/14 职场文书
股权转让协议书
2014/12/07 职场文书
给老师的一封感谢信
2015/01/20 职场文书
jquery插件实现悬浮的菜单
2021/04/24 jQuery
Python使用psutil库对系统数据进行采集监控的方法
2021/08/23 Python
关于python3 opencv 图像二值化的问题(cv2.adaptiveThreshold函数)
2022/04/04 Python
mysql幻读详解实例以及解决办法
2022/06/16 MySQL