拖拽献祭中的黑山羊-DataTransfer对象

栏目: JavaScript · 发布时间: 6年前

内容简介:这篇文章发布于 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

本文可全文转载,但需得到原作者书面许可,同时保留原作者和出处,摘要引流则随意。

一、超位魔法-任意拖拽

拖拽献祭中的黑山羊-DataTransfer对象

伟大的安兹·乌尔·恭,纳萨里克地下大坟墓的主人,四十一位无上至尊的统合者,不死者之王,魔导国魔导王,异世界overlord,在这花好月圆之夜,释放了一个超位魔法——任意拖拽!

凡是不能手写页面元素拖拽效果者,即死,10日后才能复活!顿时,无数前端开发还没意识到发生了什么,眼前一黑,全部都倒下了。

“哈哈,实在是精彩,足足秒了7万零1人,破了我昨天的记录!”恭大人兴奋地大声疾呼。

然后,很快,天空出现巨大的黑球,然后5只巨大的黑山羊幼崽出现了。

拖拽献祭中的黑山羊-DataTransfer对象

大如小山,除了怪物已经无法用其他词形容了。

剩下的开发人员手足无措,惊恐万分,顿时仓皇而逃,但都难逃一死。不死者之王,果然是至高无上的存在!

二、解构魔法-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对象

若有兴趣亲自看看控制台输出内容,可以狠狠地点击这里: 拖拽相关事件中的DataTransfer对象demo

这个DataTransfer对象包含诸多属性和方法,我们可以用来改变拖拽效果,获取和设置拖拽内容,熟练掌握,必定可以抵御献祭中的黑山羊魔法。

三、DataTransfer对象属性和方法详解

DataTransfer对象包含下面5个标准属性和4个标准方法。

标准属性

DataTransfer.dropEffect
获取当前所选拖放操作的类型,或将拖拽操作设置为新类型。值必须为 nonecopylinkmove 中的一个。
DataTransfer.effectAllowed
提供可能的所有类型的操作。必须是 nonecopycopyLinkcopyMovelinklinkMovemovealluninitialized 中的一个。
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 值分别为 movecopylinknone ,如下代码示意:

// 拖拽元素经过box
box.addEventListener('dragover', function (event) {
    event.preventDefault();
    // 设置dropEffect值为move, copy, link, none
    event.dataTransfer.dropEffect = 'move';
});

则4个不同属性值效果如下如下截图示意:

拖拽献祭中的黑山羊-DataTransfer对象 拖拽献祭中的黑山羊-DataTransfer对象 拖拽献祭中的黑山羊-DataTransfer对象 拖拽献祭中的黑山羊-DataTransfer对象

可以看到,设置不同的 dropEffect 属性值,鼠标的手形就会有不一样的表现。

眼见为实,如果您是桌面浏览器浏览,则可以狠狠地点击这里: DataTransfer.dropEffect效果演示demo

选择不同的单选选项,然后拖动图片到该容器上方,即可看到不一样的鼠标手形表现,这就是 dropEffect 目前的表现。

其他信息

dropEffect 属性的设置主要用在 dragenterdragover 事件中,同时受 effectAllowed 属性影响。

例如,我们在 dragstart 的时候设置 effectAllowed 属性值为 'none' ,则 dropEffect 只能表现为 'none' ,而不会出现其他手形,即使设置了其他手形对应的属性值。

五、DataTransfer.effectAllowed

表示拖拽允许的效果。支持的属性值较多,如下:

uninitialized
默认值,表示未初始化。从效果上来讲,和 all 是一样滴。
none
不允许拖拽。鼠标保持禁止状态。
copy
可以在新位置复制元素。
copyLink
允许复制和链接操作。
copyMove
允许复制和移动操作。
link
可以在新位置建立链接。
linkMove
允许链接和移动操作。
move
可以把元素移动到新位置。
all
什么操作都允许。

如果 effectAllowed 属性值设置为上面列表以外的其它值则没有任何效果。另外在IE浏览器下所有的字都会小写,因此类似 linkMove 会变成 linkmove

effectAllowed和dropEffect的关系

effectAlloweddropEffect 通常应用的事件方法名不一样, effectAllowed 多用在 dragstart 事件中,而 dropEffect 属性的设置主要用在 dragenterdragover 事件中。

effectAlloweddropEffect 的彼此间是有制约关系,当我们给 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页面一个灰色盒子中,如下图:

拖拽献祭中的黑山羊-DataTransfer对象

释放鼠标,此时这个链接就会在新标签页中打开。

相关代码如下:

<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页面的框框里面,结果效果如下图:

拖拽献祭中的黑山羊-DataTransfer对象

拖拽献祭中的黑山羊-DataTransfer对象

可以看到,两个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又包含多个属性和方法,属性包括 kindtype ,方法包括 getAsString()getAsFile() ,这个和剪切板item对象方法是一致的。

我们可以通过一个小案例快速了解一下这些属性含义和作用。

您可以狠狠地点击这里: DataTransferItem属性和方法信息输出demo

log信息打印测试代码如下:

console.log('kind: ' + DataTransferItem.kind + ', type: ' + DataTransferItem.type + '\n');
DataTransferItem.getAsString(function (str) {
    console.log('\ngetAsString: ' + str);    
});

主要测试了 kindtype 属性和 getAsString() 方法。

拖动图片,此时就可以得到DataTransfer.items具体都是些什么信息,如下截图:

拖拽献祭中的黑山羊-DataTransfer对象

其中,包含3个item, kind 全部都是 stringtype 指的是mimeType类型,包含3中不同类型,为: text/plaintext/htmltext/uri-list

如果我们拖动页面上文字信息到输入框上显示的就只有2中类型项:

拖拽献祭中的黑山羊-DataTransfer对象

如果从浏览器地址栏拖文字到输入框,就会只有 text/plain 这1种类型。

//zxx: 浏览器不同的输入容器会自动甄别显示内容,例如:,对于文本框,显示内容为text/plain纯文本(如本demo),如果是富文本输入框(如HTML元素 设置contenteditable属性 ),则显示内容为text/html富文本。

实际上,子item的 kind 还有可能是 'file' ,例如把微博个人主页头像拖进来:

拖拽献祭中的黑山羊-DataTransfer对象

拖拽献祭中的黑山羊-DataTransfer对象

此时,可以调用 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);
    });
});

则拖动纯图片元素输出内容如下:

拖拽献祭中的黑山羊-DataTransfer对象
text/uri-list
text/html
Files

拖动文字如下:

拖拽献祭中的黑山羊-DataTransfer对象

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,省掉了再编辑的烦恼:

拖拽献祭中的黑山羊-DataTransfer对象

相关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对象

您可以狠狠地点击这里: 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对象

您可以狠狠地点击这里: DataTransfer.setDragImage()设置拖动跟随图片demo

十三、最后的献祭魔法

果然是超位魔法,解构下来洋洋洒洒这么多内容,虽然学得辛苦,但是若能保命,拿一切都值得的。

国庆假期到来,于是回乡去探亲,没想到好死不活,遇到伟大的安兹·乌尔·恭正好对家乡的前端再次发动了超位魔法——拖拽献祭中的黑山羊,无数前端开发沉浸在假期的愉悦之中,根本没有意识到危险的到来,似乎过去的梦魇又将重新。我虽然是个小白前端,但是为了整个行业的兴盛不衰,我毅然站在了魔法的最前面,抵御魔法的攻击。

这攻势凶猛异常,我忙拿出分析解构DataTransfer对象学到的知识作为武器抵御,哇哦哦哦,身体开始剧痛,剧烈的魔法风暴从身体擦过,想刀片一样,双脚开始站立不稳,意识开始模糊,我连忙回想没日没月解构DataTransfer对象的经历,是不是遗漏了什么细节,Bing地一声,我意识到了什么,身体中间出现一道金光,护住的最重要的心脉,原来是由此及彼,解构DataTransfer联想到了其他对象,让自己同时学会了其他魔法,相互融合,威力大增,虽然依旧遍体鳞伤,但是主要心脉护住了,就不会有生命危险,只要顶住即可!

啊啊啊啊,输出全靠吼,我用尽力气,“解构全开,魔法破!!!”

轰隆一声巨响,瞬间眼前的黑暗烟消云散,我实在只撑不住,单腿跪地,大口喘气!

“你是什么人?居然能够借助我的超位祭祀魔法!”

“我……我……就是一个……”我咽了下喉咙里的血水,继续说道:“一个普通的小白前端……”

“搜噶!你叫什么名字?”

“我的名字不值得安兹大人挂齿……”

“莫非你要拒绝回答我的问题?!!”

“不敢不敢!我的名字是————————————空气!”

“好!空气,我记住你了!我今天放你一条生路,你好好提高你的魔法技能,等以后足够强大了,我特许你可以和我一战!哈哈哈哈!”

“好……多谢安兹大人!”

说完,安兹大人就通过时空门离开了。

而我“空气”也从此成了安兹大人最重视的敌人!

拖拽献祭中的黑山羊-DataTransfer对象

本文为原创文章,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。

本文地址: https://www.zhangxinxu.com/wordpress/?p=8024

(本篇完)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人工智能

人工智能

李开复、王咏刚 / 文化发展出版社 / 2017-5-10 / CNY 55.00

人工智能已经来了,它就在我们身边,几乎无处不在。 人工智能技术正在彻底改变人类的认知,重建人机相互协作的关系。史无前例的自动驾驶正在重构我们头脑中的出行地图和人类生活图景,今天的人工智能技术也正在翻译、写作、绘画等人文和艺术领域进行大胆的尝试。 我们真的知道什么是人工智能吗? 我们真的准备好与人工智能共同发展了吗? 我们该如何在心理上将人和机器摆在正确的位置? 我们该......一起来看看 《人工智能》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码