谈谈对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 相关文章推荐
用函数式编程技术编写优美的 JavaScript_ibm
May 16 Javascript
兼容IE、FireFox、Chrome等浏览器的xml处理函数js代码
Nov 30 Javascript
跟我学习javascript的基本类型和引用类型
Nov 16 Javascript
jQuery点击按钮弹出遮罩层且内容居中特效
Dec 14 Javascript
jquery.cookie.js用法实例详解
Dec 25 Javascript
基于JavaScript实现轮播图代码
Jul 14 Javascript
Angular的模块化(代码分享)
Dec 26 Javascript
利用VUE框架,实现列表分页功能示例代码
Jan 12 Javascript
微信小程序实现登录页云层漂浮的动画效果
May 05 Javascript
小程序getLocation需要在app.json中声明permission字段
Apr 04 Javascript
JavaScript 正则应用详解【模式、欲查、反向引用等】
May 13 Javascript
小程序中手机号识别的示例
Dec 14 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
修改WordPress中文章编辑器的样式的方法详解
2015/12/15 PHP
讨论html与javascript在浏览器中的加载顺序问题
2013/11/27 Javascript
javascript自定义函数参数传递为字符串格式
2014/07/29 Javascript
JQuery设置时间段下拉选择实例
2014/12/30 Javascript
jquery事件preventDefault()方法用法实例
2015/01/16 Javascript
js实现时间显示几天前、几小时前或者几分钟前的方法集锦
2015/05/29 Javascript
js类式继承与原型式继承详解
2016/04/07 Javascript
微信小程序 底部导航栏目开发资料
2016/12/05 Javascript
原生js实现鼠标跟随效果
2017/02/28 Javascript
用JS编写一个函数,返回数组中重复出现过的元素(实例)
2017/09/14 Javascript
Angular实现的简单查询天气预报功能示例
2017/12/27 Javascript
用jquery获取select标签中选中的option值及文本的示例
2018/01/25 jQuery
node.js自动上传ftp的脚本分享
2018/06/16 Javascript
微信小程序 弹窗输入组件的实现解析
2019/08/12 Javascript
Vue中的循环及修改差值表达式的方法
2019/08/29 Javascript
vue路由传参三种基本方式详解
2019/12/09 Javascript
Python引用(import)文件夹下的py文件的方法
2014/08/26 Python
python简单文本处理的方法
2015/07/10 Python
Python做简单的字符串匹配详解
2017/03/21 Python
对python中大文件的导入与导出方法详解
2018/12/28 Python
Python使用paramiko操作linux的方法讲解
2019/02/25 Python
python算法与数据结构之单链表的实现代码
2019/06/27 Python
解决pycharm 工具栏Tool中找不到Run manager.py Task的问题
2019/07/01 Python
基于pytorch padding=SAME的解决方式
2020/02/18 Python
详解python内置模块urllib
2020/09/09 Python
高中毕业自我鉴定范文
2013/10/02 职场文书
大学生职业生涯设计书
2014/01/02 职场文书
写给女朋友的检讨书
2014/01/28 职场文书
房地产开盘策划方案
2014/02/10 职场文书
2014年行政助理工作总结
2014/11/19 职场文书
党员违纪检讨书
2015/05/05 职场文书
运动会广播稿20字
2015/08/19 职场文书
高中班主任培训心得体会
2016/01/07 职场文书
python数字转对应中文的方法总结
2021/08/02 Python
springboot读取nacos配置文件
2022/05/20 Java/Android