拖拽献祭中的黑山羊-DataTransfer对象
栏目: JavaScript · 发布时间: 7年前
内容简介:这篇文章发布于 2018年09月30日,星期日,22:10,归类于JS API。 阅读 201 次, 今日 186 次byzhangxinxu from本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。
这篇文章发布于 2018年09月30日,星期日,22:10,归类于JS API。 阅读 201 次, 今日 186 次
byzhangxinxu from https://www.zhangxinxu.com/wordpress/?p=8024
本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。
一、超位魔法-任意拖拽
伟大的安兹·乌尔·恭,纳萨里克地下大坟墓的主人,四十一位无上至尊的统合者,不死者之王,魔导国魔导王,异世界overlord,在这花好月圆之夜,释放了一个超位魔法——任意拖拽!
凡是不能手写页面元素拖拽效果者,即死,10日后才能复活!顿时,无数前端开发还没意识到发生了什么,眼前一黑,全部都倒下了。
“哈哈,实在是精彩,足足秒了7万零1人,破了我昨天的记录!”恭大人兴奋地大声疾呼。
然后,很快,天空出现巨大的黑球,然后5只巨大的黑山羊幼崽出现了。
大如小山,除了怪物已经无法用其他词形容了。
剩下的开发人员手足无措,惊恐万分,顿时仓皇而逃,但都难逃一死。不死者之王,果然是至高无上的存在!
二、解构魔法-DataTransfer对象
大家好,我是一个小白前端,因为昨天陪女朋友去现充世界赏月,没有参加这场战役,所以幸免于难。但现在还是心有余悸,因为自己也不会手写拖拽功能,如果自己在场,怕是女朋友就是别人的了。
虽庆幸,仍不安。要是下次伟大的安兹·乌尔·恭,不死者之王再释放这个“任意拖拽”超位魔法,那我岂不是死翘翘了?不行,为了活命,我必须对这个超位魔法进行解构,了解之学习之破解之。
皇天不负有心人,经过七七四十九天废寝忘食的研究,终于解构出了这个超位魔法的关键所在——DataTransfer对象,只要弄动DataTransfer对象,什么献祭中的黑山羊的幼崽都只是可爱的小羊而已,不足为惧。
为了让前台前端同僚也可以幸免于难,于是,我冒着被暴露的风险向大家传授DataTransfer对象相关知识。
DataTransfer对象的出现
DataTransfer 对象出现在拖拽事件中,具体包括开始拖拽 dragstart 事件,拖拽进入 dragenter 事件,拖拽离开 dragleave 事件,拖拽经过 dragover 事件,拖拽释放 drop 事件以及拖拽结束 dragend 事件。
示意代码如下:
document.addEventListener('dragstart', function (event) {
console.log('dragstart: ' + event.dataTransfer);
});
document.addEventListener('dragenter', function (event) {
console.log('dragenter: ' + event.dataTransfer);
});
document.addEventListener('dragleave', function (event) {
console.log('dragleave: ' + event.dataTransfer);
});
document.addEventListener('dragover', function (event) {
event.preventDefault();
console.log('dragover: ' + event.dataTransfer);
});
document.addEventListener('drop', function (event) {
console.log('drop: ' + event.dataTransfer);
});
document.addEventListener('dragend', function (event) {
console.log('dragend: ' + event.dataTransfer);
});
假设包含上面JavaScript代码的页面有一张图片,拖动这张图片,就可以在控制台控制看到类似下面截图的输出:
若有兴趣亲自看看控制台输出内容,可以狠狠地点击这里: 拖拽相关事件中的DataTransfer对象demo
这个DataTransfer对象包含诸多属性和方法,我们可以用来改变拖拽效果,获取和设置拖拽内容,熟练掌握,必定可以抵御献祭中的黑山羊魔法。
三、DataTransfer对象属性和方法详解
DataTransfer对象包含下面5个标准属性和4个标准方法。
标准属性
- DataTransfer.dropEffect
-
获取当前所选拖放操作的类型,或将拖拽操作设置为新类型。值必须为
none,copy,link或move中的一个。 - DataTransfer.effectAllowed
-
提供可能的所有类型的操作。必须是
none,copy,copyLink,copyMove,link,linkMove,move,all或uninitialized中的一个。 - DataTransfer.files
- 拖拽的本地文件列表。如果拖动操作不涉及拖动文件,则此属性为空列表。
- DataTransfer.items (只读)
- 提供DataTransferItemList对象,该对象是所有拖动数据的列表。
- DataTransfer.types (只读)
-
在
dragstart事件中设置数据格式,返回的是一个字符串数组。
标准方法
- DataTransfer.clearData([format])
-
删除与给定类型关联的数据。
format参数是可选的。如果类型为空或未指定,则删除所有关联的数据。如果指定类型的数据不存在,或者数据传输不包含任何数据,则此方法无效。 - DataTransfer.getData(format)
- 返回给定类型的数据,如果该类型的数据不存在或数据传输不包含数据,则返回空字符串。
- DataTransfer.setData(format, data)
- 设置给定类型的数据。如果该类型的数据不存在,则在末尾添加,以使列表中的最后一项成为新格式类型。如果该类型的数据已存在,则在相同位置把现有数据替换掉。
- DataTransfer.setDragImage(img, xOffset, yOffset)
- 设置用于拖动的自定义图像。
下面一个一个详细展开。
四、DataTransfer.dropEffect
dropEffect 属性顾名思意拖拽效果,在PC web端主要表现在鼠标手形上。不同的 dropEffect 值,鼠标的手形效果是不一样的。
举个例子,假设有个 box 元素,当 dragover 的时候,我们设置其 dropEffect 值分别为 move , copy , link 和 none ,如下代码示意:
// 拖拽元素经过box
box.addEventListener('dragover', function (event) {
event.preventDefault();
// 设置dropEffect值为move, copy, link, none
event.dataTransfer.dropEffect = 'move';
});
则4个不同属性值效果如下如下截图示意:
可以看到,设置不同的 dropEffect 属性值,鼠标的手形就会有不一样的表现。
眼见为实,如果您是桌面浏览器浏览,则可以狠狠地点击这里: DataTransfer.dropEffect效果演示demo
选择不同的单选选项,然后拖动图片到该容器上方,即可看到不一样的鼠标手形表现,这就是 dropEffect 目前的表现。
其他信息
dropEffect 属性的设置主要用在 dragenter 和 dragover 事件中,同时受 effectAllowed 属性影响。
例如,我们在 dragstart 的时候设置 effectAllowed 属性值为 'none' ,则 dropEffect 只能表现为 'none' ,而不会出现其他手形,即使设置了其他手形对应的属性值。
五、DataTransfer.effectAllowed
表示拖拽允许的效果。支持的属性值较多,如下:
- uninitialized
-
默认值,表示未初始化。从效果上来讲,和
all是一样滴。 - none
- 不允许拖拽。鼠标保持禁止状态。
- copy
- 可以在新位置复制元素。
- copyLink
- 允许复制和链接操作。
- copyMove
- 允许复制和移动操作。
- link
- 可以在新位置建立链接。
- linkMove
- 允许链接和移动操作。
- move
- 可以把元素移动到新位置。
- all
- 什么操作都允许。
如果 effectAllowed 属性值设置为上面列表以外的其它值则没有任何效果。另外在IE浏览器下所有的字都会小写,因此类似 linkMove 会变成 linkmove 。
effectAllowed和dropEffect的关系
effectAllowed 和 dropEffect 通常应用的事件方法名不一样, effectAllowed 多用在 dragstart 事件中,而 dropEffect 属性的设置主要用在 dragenter 和 dragover 事件中。
effectAllowed 和 dropEffect 的彼此间是有制约关系,当我们给 effectAllowed 设置了对应的属性值,则 dropEffect 只能设置为 effectAllowed 允许的值,否则是无效的。
举个例子,如果我们设置 effectAllowed 值为 'copyMove' ,则 dropEffect 只有 'copy' 和 'move' 这两个属性值才有效。
effectAllowed 看上去很屌,但实际应用的时候相当鸡肋。我们通常的拖拽应用是用不到这个的。只要下面这个场景,那就是当我们有很多个元素需要拖拽,但是需要像垃圾一样分门别类的时候, effectAllowed 就有用了。
原生拖拽事件有这样一种行为,那就是如果 effectAllowed 值和 dropEffect 值不一致,则无法响应 drop 事件。我们可以想象一下,假设我们在网页中放三个垃圾箱,分别回收move元素,copy元素和link元素,由于DOM元素的转移或者复制我们都是在 drop 事件中完成的,则 effectAllowed 包含 copy 元素的才能拖拽进入copy垃圾箱(可以触发 drop 事件)。
平常开发都是简单的拖拽,哪里需要用到分门别类啊,因此 effectAllowed 也就无用武之地了。
//zxx: 这里有个 CodePen ,可以感受下。
link作为属性值案例一则
这里插播一个案例,实现的是Chrome浏览器下拖拽链接也能新窗口打开的实现。在Firefox和IE浏览器下,链接元素你按住一拖拽,就可以在新的浏览器标签页中打开,Chrome浏览器则需要拖动到地址栏才可以。如果我们想要实现Chrome浏览器下拖拽也能标签页中打开,就可以看看下面这个案例。
您可以狠狠地点击这里: 拖拽链接新标签页打开demo
拖拽链接到demo页面一个灰色盒子中,如下图:
释放鼠标,此时这个链接就会在新标签页中打开。
相关代码如下:
<a href="#" id="link">拖拽我到下面框框试试</a> <p id="box" class="box"></p>
var isOpenLink = null;
link.addEventListener('dragstart', function (event) {
event.dataTransfer.effectAllowed = 'link';
});
box.addEventListener('dragover', function (event) {
event.preventDefault();
// 检测是否需要新窗口打开链接
if (event.dataTransfer.dropEffect != 'link') {
isOpenLink = true;
}
event.dataTransfer.dropEffect = 'link';
});
box.addEventListener('drop', function (event) {
event.preventDefault();
if (isOpenLink) {
window.open(link.href);
}
});
通过检测 dataTransfer.dropEffect ,有效避免和原本支持拖拽新标签页打开链接的浏览器冲突。
六、DataTransfer.files
当我们从桌面往网页浏览器中拖文件的时候,DataTransfer.files就派上用场了,其对应的列表只就是我们拖进去的文件列表。
目前在实际开发中应用的比较多的是拖拽上传,具体可参见我11年写的一个ajax图片上传demo。
这里我又写了一个更精简的demo,演示如何使用DataTransfer.files获得桌面拖拽进入的文件信息。
您可以狠狠的点击这里: DataTransfer.files获取桌面文件信息demo
例如,我框选两个文本文件和两个快捷方式,拖到demo页面的框框里面,结果效果如下图:
可以看到,两个txt文件识别为了text/plain,而快捷方式文件没有mime-type,因此,输出内容是空。
相关JS代码如下:
box.addEventListener('drop', function (event) {
event.preventDefault();
// 遍历文件信息
var files = event.dataTransfer.files || [];
var length = files.length;
if (length == 0) {
this.innerHTML = '<p>没有文件</p>';
return;
}
var html = '';
for (var index = 0; index < length; index++) {
html += '<p>类型:'+ files[index].type +'</p>';
}
this.innerHTML = html;
});
什么时候会出现“没有文件”的提示呢?比方说demo页面你框选几个文字,拖拽到方框框里面,就会提示“没有文件”了,因为你拖拽进去的本来就不是文件内容。不过,这个拖拽内容我们是可以使用DataTransfer.items获取的。
七、DataTransfer.items
DataTransfer.items可以用来获取拖拽的数据信息,只读。
DataTransfer.items为DataTransferItem类型的数据列表集合,而DataTransferItem又包含多个属性和方法,属性包括 kind 和 type ,方法包括 getAsString() 和 getAsFile() ,这个和剪切板item对象方法是一致的。
我们可以通过一个小案例快速了解一下这些属性含义和作用。
您可以狠狠地点击这里: DataTransferItem属性和方法信息输出demo
log信息打印测试代码如下:
console.log('kind: ' + DataTransferItem.kind + ', type: ' + DataTransferItem.type + '\n');
DataTransferItem.getAsString(function (str) {
console.log('\ngetAsString: ' + str);
});
主要测试了 kind 和 type 属性和 getAsString() 方法。
拖动图片,此时就可以得到DataTransfer.items具体都是些什么信息,如下截图:
其中,包含3个item, kind 全部都是 string , type 指的是mimeType类型,包含3中不同类型,为: text/plain , text/html 和 text/uri-list 。
如果我们拖动页面上文字信息到输入框上显示的就只有2中类型项:
如果从浏览器地址栏拖文字到输入框,就会只有 text/plain 这1种类型。
//zxx: 浏览器不同的输入容器会自动甄别显示内容,例如:,对于文本框,显示内容为text/plain纯文本(如本demo),如果是富文本输入框(如HTML元素 设置contenteditable属性 ),则显示内容为text/html富文本。
实际上,子item的 kind 还有可能是 'file' ,例如把微博个人主页头像拖进来:
此时,可以调用 getAsFile() 方法将其转换成二进制文件对象,然后可以ajax上传等处理。
实际开发的处理模板
有个处理模板,无论何种数据类型,大家可以找到对应位置,进行相应的处理,省掉不少自己写逻辑判断的麻烦。
handleDataTransferItems = function (items) {
for (var i = 0; i < items.length; i += 1) {
var kind = items[i].kind;
var type = items[i].type;
// 逻辑开始
if (kind == 'string') {
if (type.indexOf('text/plain') != -1) {
items[i].getAsString(function (str) {
// str是纯文本,该怎么处理,就在这里处理
});
} else if (type.indexOf('text/html') != -1) {
items[i].getAsString(function (str) {
// str是富文本,就在这里处理
});
} else if (type.indexOf('text/uri-list') != -1) {
items[i].getAsString(function (str) {
// str是uri地址,在这里进行处理
});
}
} else if (kind == 'file') {
// 如果是图片
if (type.indexOf('image/') != -1) {
var file = items[i].getAsFile();
// file就是图片文件对象,可以上传,或者其他处理
}
}
}
};
使用示例:
document.addEventListener('drop', function (event) {
var items = event.dataTransfer.items || [];
handleDataTransferItems(items);
});
近亲clipboardData.items
剪切板对象也有items属性,数据类型以及item子项的属性和方法和DataTransfer对象是一样的,学会了一个自然也就学会了另外一个,不展开。
八、DataTransfer.types
DataTransfer.types用在 dragstart 事件中,包含拖拽内容的包含的mimeType类型们,可以遍历出具体的 type 类型。假设页面有如下全局JS代码:
document.addEventListener('dragstart', function (event) {
// 遍历并输出拖拽内容的类型
var types = event.dataTransfer.types || [];
[].slice.call(types).forEach(function (type) {
console.log(type);
});
});
则拖动纯图片元素输出内容如下:
text/uri-list text/html Files
拖动文字如下:
text/html text/plain
眼见为实,您可以狠狠地点击这里: DataTransfer.types使用测试demo
DataTransfer.types有什么用呢?如果我们希望页面某些元素可以拖拽,有些不允许,则可以使用types属性进行检测,例如,如果是包含Files,则执行 event.preventDefault() ,拖拽行为就会被禁止。
九、DataTransfer.getData()
DataTransfer.getData(format)可以快捷获取拖拽的内容。
format 可以理解为就是DataTransfer.items中的type值,例如,我们想要获取拖拽内容的富文本格式,可以:
document.addEventListener('drop', function (event) {
// 获取拖拽富文本内容
var html = event.dataTransfer.getData('text/html');
});
如果是获取纯文本,可以 text/plain ,或者有时候 text/uri-list ,实际开发时候,我们会直接使用 'text' ,例如:
document.addEventListener('drop', function (event) {
// 获取拖拽纯文本内容
var html = event.dataTransfer.getData('text');
});
优化输入框拖拽输入体验
本案例类似于“ 利用剪切板JS API优化输入框的粘贴体验 ”这篇文章,当我们拖拽本文进入输入框的时候,文本信息可能包含不需要的信息,例如手机号中的334分隔符,借助 getData() 方法我们可以进行过滤和优化。
您可以狠狠地点击这里: DataTransfer.getData()优化手机号拖拽输入体验demo
可以看到(如下GIF),手机号明明是132-0803-3621,但拖到输入框后自动变成了13208033621,省掉了再编辑的烦恼:
相关JavaScript代码如下:
input.addEventListener('drop', function (event) {
// 获取拖拽文本内容
var text = event.dataTransfer.getData('text');
if (this.value == '' && text.match(/\d/g) && text.match(/\d/g).length == 11) {
event.preventDefault();
input.value = text.replace(/\D/g, '');
input.select();
}
});
十、DataTransfer.setData()
DataTransfer.setData(format, data)可以自定义拖拽的内容信息。可以重置原生的拖拽内容,或者用来参数传递。
例如如果我们页面运行了如下JS代码:
document.addEventListener('dragstart', function (event) {
// 重置拖拽文本内容
event.dataTransfer.setData('text', '张鑫旭-鑫空间-鑫生活');
});
则所有内容拖拽进入输入框内容都是“张鑫旭-鑫空间-鑫生活”,如下GIF示意:
您可以狠狠地点击这里: DataTransfer.setData()重置拖拽文本内容demo
我们实际开发基本上都是拖拽具体模块元素,此时 setData() 方法多用来传递拖拽元素的 id 。
十一、DataTransfer.clearData()
DataTransfer.clearData()只能用在 dragstart 事件中,会清除所有的数据。也就是执行了此方法后,DataTransfer.getData()方法只能获得空字符串。
使用场景不是很多。
当我们需要使用 setData() 方法自定义拖拽数据的时候,为了避免原生拖拽数据干扰,可以先执行一次 clearData() 方法。这样可以避免 text/html 类型数据干扰(如果我们自定义的是纯文本数据)。
十二、DataTransfer.setDragImage()
DataTransfer.setDragImage(img, offsetX, offsetY)用在 dragstart 事件中,可以设置拖拽时候有个图片跟在后面。
其中, img 表示图片DOM元素对象, offsetX 表示距离鼠标的水平偏移距离, offsetY 表示距离鼠标的垂直偏移距离。
直接看一个例子:
var image = new Image();
image.src = './1f603.svg';
document.addEventListener('dragstart', function (event) {
// 设置拖拽图片
event.dataTransfer.setDragImage(image, 10, 10);
});
此时,页面上拖拽任意内容,就会看到一个笑脸跟在后面了,例如:
您可以狠狠地点击这里: DataTransfer.setDragImage()设置拖动跟随图片demo
十三、最后的献祭魔法
果然是超位魔法,解构下来洋洋洒洒这么多内容,虽然学得辛苦,但是若能保命,拿一切都值得的。
国庆假期到来,于是回乡去探亲,没想到好死不活,遇到伟大的安兹·乌尔·恭正好对家乡的前端再次发动了超位魔法——拖拽献祭中的黑山羊,无数前端开发沉浸在假期的愉悦之中,根本没有意识到危险的到来,似乎过去的梦魇又将重新。我虽然是个小白前端,但是为了整个行业的兴盛不衰,我毅然站在了魔法的最前面,抵御魔法的攻击。
这攻势凶猛异常,我忙拿出分析解构DataTransfer对象学到的知识作为武器抵御,哇哦哦哦,身体开始剧痛,剧烈的魔法风暴从身体擦过,想刀片一样,双脚开始站立不稳,意识开始模糊,我连忙回想没日没月解构DataTransfer对象的经历,是不是遗漏了什么细节,Bing地一声,我意识到了什么,身体中间出现一道金光,护住的最重要的心脉,原来是由此及彼,解构DataTransfer联想到了其他对象,让自己同时学会了其他魔法,相互融合,威力大增,虽然依旧遍体鳞伤,但是主要心脉护住了,就不会有生命危险,只要顶住即可!
啊啊啊啊,输出全靠吼,我用尽力气,“解构全开,魔法破!!!”
轰隆一声巨响,瞬间眼前的黑暗烟消云散,我实在只撑不住,单腿跪地,大口喘气!
“你是什么人?居然能够借助我的超位祭祀魔法!”
“我……我……就是一个……”我咽了下喉咙里的血水,继续说道:“一个普通的小白前端……”
“搜噶!你叫什么名字?”
“我的名字不值得安兹大人挂齿……”
“莫非你要拒绝回答我的问题?!!”
“不敢不敢!我的名字是————————————空气!”
“好!空气,我记住你了!我今天放你一条生路,你好好提高你的魔法技能,等以后足够强大了,我特许你可以和我一战!哈哈哈哈!”
“好……多谢安兹大人!”
说完,安兹大人就通过时空门离开了。
而我“空气”也从此成了安兹大人最重视的敌人!
本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址: https://www.zhangxinxu.com/wordpress/?p=8024
(本篇完)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Algorithm Design
Jon Kleinberg、Éva Tardos / Addison-Wesley / 2005-3-26 / USD 144.20
Algorithm Design introduces algorithms by looking at the real-world problems that motivate them. The book teaches students a range of design and analysis techniques for problems that arise in compu......一起来看看 《Algorithm Design》 这本书的介绍吧!