html5拖放原理,HTML5 拖放实现

对于拖拽我们并不陌生,尤其是在 HTML5 支持拖拽后,让这一切变的更加容易了。本文旨在探讨 H5 拖拽方案、实现步骤,以及相关原理。

一、我理解的拖拽

为了更好的说明,这里我强行分类,大致如下:

操作类拖拽,譬如,拖拽上传;

功能性拖拽,拖拽排序,具有指向性,位置互换;

使用方便型,对某些固定元素,使其随处可放;

拖拽实现的思路都是一致的,唯一差别就是表现形式。

二、直接上手

让我们逐步深入,从示例开始吧(文中所用示例地址)!

1.首先,设置可拖动元素,我们使用 draggable

1.png复制代码

为了方便下文具体说明,这里我添加了 id 和 ondragstart 事件,如下:

1.png复制代码

ondragstart 属性调用了一个函数,drag(event),它规定了被拖动的数据。

2.然后,规定当元素被拖动时,会发生什么。关键词 setData()

function drag(ev)

{

ev.dataTransfer.setData('Text',ev.target.id);

}复制代码

dataTransfer.setData() 方法设置被拖数据的数据类型和值,后面我会详细说明。

这里需要注意 setData 的数据类型是 ‘Text’,值是可拖动元素的 id 。

3.最后,放到何处,ondragover 事件规定在何处放置被拖动的数据,当放置被拖数据时 ondrop 会被触发。

function drop(ev)

{

ev.preventDefault();

var data=ev.dataTransfer.getData("Text");

ev.target.appendChild(document.getElementById(data));

}

function allowDrop(ev)

{

ev.preventDefault();

}复制代码

这里需要注意几个问题:

默认地,无法将数据/元素放置到其他元素中。如果需要设置允许放置,我们必须阻止对元素的默认处理方式,即通过调用 ondragover 事件的 event.preventDefault() 方法;

调用 preventDefault() 来避免浏览器对数据的默认处理(drop 事件的默认行为是以链接形式打开);

通过 dataTransfer.getData(‘Text’) 方法获得被拖的数据。该方法将返回在 setData() 方法中设置为相同类型的任何数据。

三、来回拖放

到这里我们已经可以将一个元素拖到指定容器中,那么如何让元素在两个容器中来回拖放呢?

继续保持之前的思路,我们做一点简单的调整:

1.首先,确保有俩元素;

.jartto1,

.jartto2 {

float: left;

width: 150px;

height: 140px;

margin: 10px;

border-radius: 3px;

border: 1px solid #aaaaaa;

}复制代码

2.更改 html 结构

2.png

draggable="true" οndragstart="drag(event)" />

3.触发方法

function allowDrop(ev) {

ev.preventDefault();

}

function drag(ev) {

ev.dataTransfer.setData('Text', ev.target.id);

}

function drop(ev) {

ev.preventDefault();

var data = ev.dataTransfer.getData('Text');

ev.target.appendChild(document.getElementById(data));

}复制代码

代码看着变化并不大,重点其实在这里:

2.png

draggable="true" οndragstart="drag(event)" />

复制代码

可以看到,我们对两个容器都设置了 ondrop 和 ondragover , 这样两个容器都允许放置,也就实现了来回拖放。

嗯,来回拖拽也实现了,我们继续升级,来看看随处拖放。这里稍微复杂一点,先简单梳理一下思路:

1.首先设置 dragover 事件作用于整个 document,确保元素可以随处放置;

2.在 dragstart 事件中计算鼠标拖拽的位置;

3.在 drop 事件中将上步计算的 offset 和 clientX,clientY 运算,计算出元素放置位置;

这里我以 Angularjs 举例说明,其他框架实现形式可能有差别,仅作为参考。

首先,创建指令,并设置作用元素的 draggable 属性;

angular.module('app')

.directive('dragdrop', [function () {

return {

link: (scope, element, attrs) => {

var el = element[0];

el.draggable = true;

}

};

}

]);复制代码

其次,存储拖拽数据;

el.addEventListener('dragstart',

function(e) {

let style = window.getComputedStyle(e.target, null);

e.dataTransfer.setData('text/plain',

`${(parseInt(style.getPropertyValue('left')) - e.clientX)},${(parseInt(style.getPropertyValue('top')) - e.clientY)}`

);

},false);复制代码

然后,我们必须阻止对元素的默认处理方式,从而使元素可以放置到其他元素中;

document.body.addEventListener('dragover',function(e) {

e.preventDefault();

return false;

},false);复制代码

再次,将拖拽数据取出,重新确定元素位置;

document.body.addEventListener('drop',function(e) {

let offset = e.dataTransfer.getData('text/plain').split(',');

el.style.left = (e.clientX + parseInt(offset[0])) + 'px';

el.style.top = (e.clientY + parseInt(offset[1])) + 'px';

e.preventDefault();

return false;

},false);复制代码

最后,我们可以这样使用:

其实我们还可以在拖拽的过程中做一些样式处理(加类减类),以获取更好的用户体验效果,但这并不是必要的,所以代码中并没有体现。

当然,我们也可以通过 mouse 事件来实现,如下:

element.on('mousedown', function(event) {

// Prevent default dragging of selected content

event.preventDefault();

startX = event.pageX - x;

startY = event.pageY - y;

$document.on('mousemove', mousemove);

$document.on('mouseup', mouseup);

});复制代码

这里只展示了代码片段,详情请查看,demo6 示例代码

五、细节问题处理

整个过程看起来相当顺利,其实还是有很多细节问题的,这里简单罗列一下:

第一,fix 定位的元素,拖拽会失效;

第二,容器存在包含关系,除非你要 drag everywhere,否则拖拽将失效;

第三,需要阻止浏览器的默认行为,譬如重定向;

第四,默认地,无法将数据/元素放置到其他元素中。如果需要设置允许放置,我们必须阻止对元素的默认处理方式,即通过调用 ondragover 事件的 event.preventDefault() 方法;

第五,移动兼容较差,慎重选择;

六、drag 和 drop 事件

我们列举一下 drag 和 drop 事件,意思很明了,这里就不赘述了。

drag

dragstart

dragend

dragover

dragenter

dragleave

drop

我们在 drag everywhere 例子中使用到了 DataTransfer.setData() ,再熟悉一下。DataTransfer.setData() 方法用来设置拖放操作的 drag data 到指定的数据和类型。

如果给定类型的数据不存在,则将其添加到拖动数据存储的末尾,使得 types 列表中的最后一个项目将是新类型。

如果给定类型的数据已经存在,现有数据将被替换为相同的位置。也就是说,替换相同类型的数据时 types列表的顺序不会更改。

示例数据类型为:"text/plain" 和 "text/uri-list"

语法:

void dataTransfer.setData(format, data);复制代码

参数:

format: 一个DOMString 表示要添加到 drag object的拖动数据的类型。

data: 一个 DOMString表示要添加到 drag object的数据。

八、Window.getComputedStyle()

除了 DataTransfer.setData() 这里我还要介绍一个特别有意思的属性,Window.getComputedStyle(),为什么这么说呢?先来看一下他的文档:

Window.getComputedStyle() 方法给出应用活动样式表后的元素的所有CSS属性的值,并解析这些值可能包含的任何基本计算。

语法:

let style = window.getComputedStyle(element, [pseudoElt]);复制代码

参数:

element:用于获取计算样式的Element

pseudoElt:可选,指定一个要匹配的伪元素的字符串。必须对普通元素省略(或null)。

强大之处就在于:它返回的样式是一个实时的 CSSStyleDeclaration 对象,当元素的样式更改时,它会自动更新本身。

至于兼容性,请看下图:

491f15723d6cd9ccd0c687bb7d1b56d0.png

九、getPropertyValue

The CSSStyleDeclaration.getPropertyValue() method interface returns a DOMString containing the value of a specified CSS property.

语法:

var value = style.getPropertyValue(property);复制代码

示例:

var declaration = document.styleSheets[0].cssRules[0].style;

var value = declaration.getPropertyValue('margin'); // "1px 2px"复制代码

额外扩展了一个属性和两个方法,无非想提供更多的实现思路,从而使我们的开发更加高效。这不是考点,考试不会考哦~

十、插件推荐

在自己尝试实现 drag 之前很长一段时间,我都在使用一些成熟插件,这里推荐几个:

类似的插件网上一搜一大堆,前端大军造轮子的能力还是很强的,这不,我们今天又造了一个🙈~

十一、参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值