谈谈对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 相关文章推荐
解决js正则匹配换行问题实现代码
Dec 10 Javascript
通过js为元素添加多项样式,浏览器全兼容写法
Aug 30 Javascript
jQuery插件multiScroll实现全屏鼠标滚动切换页面特效
Apr 12 Javascript
js 获取元素在页面上的偏移量的方法汇总
Apr 13 Javascript
gameboy网页闯关游戏(riddle webgame)--仿微信聊天的前端页面设计和难点
Feb 21 Javascript
浅谈EasyUi ComBotree树修改 父节点选择的问题
Nov 07 Javascript
js仿QQ邮箱收件人选择与搜索功能
Feb 10 Javascript
jQuery中绑定事件bind() on() live() one()的异同
Feb 23 Javascript
Angularjs+bootstrap+table多选(全选)支持单击行选中实现编辑、删除功能
Mar 27 Javascript
three.js实现炫酷的全景3D重力感应
Dec 30 Javascript
详解vue 不同环境配置不同的打包命令
Apr 07 Javascript
vue中 this.$set的使用详解
Nov 17 Vue.js
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 smarty 二级分类代码和模版循环例子
2011/06/01 PHP
PHP手机号码归属地查询代码(API接口/mysql)
2012/09/04 PHP
PHP计算一年多少个星期和每周的开始和结束日期
2014/07/01 PHP
PHP中的命名空间相关概念浅析
2015/01/22 PHP
php对文件夹进行相关操作(遍历、计算大小)
2015/11/04 PHP
一个很简单的办法实现TD的加亮效果.
2006/06/29 Javascript
jQuery EasyUI API 中文文档 - Menu菜单
2011/10/03 Javascript
网页编辑器ckeditor和ckfinder配置步骤分享
2012/05/24 Javascript
JS数学函数Exp使用说明
2012/08/09 Javascript
jquery动画3.创建一个带遮罩效果的图片走廊
2012/08/24 Javascript
jquery队列queue与原生模仿其实现方法分享
2014/03/25 Javascript
iframe调用父页面函数示例详解
2014/07/17 Javascript
bootstrap-treeview自定义双击事件实现方法
2016/01/09 Javascript
微信小程序使用第三方库Underscore.js步骤详解
2016/09/27 Javascript
输入框点击时边框变色效果的实现方法
2016/12/26 Javascript
JavaScript函数柯里化原理与用法分析
2017/03/31 Javascript
jQuery实现锚点向下平滑滚动特效示例
2017/08/29 jQuery
常用的 JS 排序算法 整理版
2018/04/05 Javascript
使用JavaScript破解web
2018/09/28 Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
2018/12/12 Javascript
详解在Node.js中发起HTTP请求的5种方法
2019/01/10 Javascript
vue组件数据传递、父子组件数据获取,slot,router路由功能示例
2019/03/19 Javascript
原生js代码能实现call和bind吗
2019/07/31 Javascript
js实现旋转木马轮播图效果
2020/01/10 Javascript
JS实现网站楼层导航效果代码实例
2020/06/16 Javascript
使用python将mdb数据库文件导入postgresql数据库示例
2014/02/17 Python
详解Python进程间通信之命名管道
2017/08/28 Python
Python 输入一个数字判断成绩分数等级的方法
2018/11/15 Python
对python中各个response的使用说明
2020/03/28 Python
python pyg2plot的原理知识点总结
2021/02/28 Python
css3实现wifi信号逐渐增强效果实例
2017/08/09 HTML / CSS
巴西家用小家电购物网站:Polishop
2016/08/07 全球购物
转预备党员政审材料
2014/02/06 职场文书
主持稿开场白
2015/06/01 职场文书
老乡会致辞
2015/07/28 职场文书
2022年四月新番
2022/03/15 日漫