drag'n'drop是HTML5时代装逼必备技能之一,API相关介绍参见强大的MDN,然而新功能总是有很多坑的,让我们来挖一挖。
首先简单描述一下整个流程:
- 给想要支持drag的元素(暂且叫做A)加一个
draggable="true"
的attribute。 - 拖拽开始时,A触发
dragstart
事件,如果这时执行event.preventDefault()
,则停止拖拽。 - 在拖拽的过程中,拖拽所到之处的元素(把它叫做B)会触发
dragenter
、dragover
事件,事件中运行event.preventDefault()
才能drop,否则是不会触发drop
事件的。 - 拖拽结束时,如果可以drop,则在B上会触发
drop
事件。 - 最后在A上触发
dragend
事件。

这个流程中其实是有很多坑的,下面一点点地挖:
- 我们知道,很多时候,HTML元素的属性值与名字相同的时候可以省略值的部分,比如
<option value="1" selected="selected">1</option>
可以写成<option value="1" selected>1</option>
,然而,draggable="true"
的值并不是draggable
,所以是不能省的! -
dragstart
事件中,我们通常需要记录一些被拖拽元素的信息,event
本身有一个event.dataTransfer.setData(format, data)
方法,但是只支持简单的DOMString类型数据,一般来说是不够用的,不如将相关数据另外保存到一个对象中。
这个时候问题就来了:- 不写
event.dataTransfer.setData(format, data)
,Chrome下正常,Firefox不能拖拽。 - 写
event.dataTransfer.setData('text/plain', '')
,Chrome和Firefox正常,IE报错参数异常。 - 改成
event.dataTransfer.setData('text', '')
,因为IE只支持format
为text
,这下Chrome、Firefox、IE都正常了,但是Vivaldi不能拖拽。 - 最后改成
event.dataTransfer.setData('text', 0)
,因为Vivaldi下data
不能为空,终于兼容各大现代浏览器了。
- 不写
- 然后到了
dragenter
和dragover
的部分,由于要不断验证数据来判断是否可拖拽,自然就选用了一个dragover
事件,当数据合法时event.preventDefault()
,然后在除IE以外的浏览器上都正常,而IE依然是不能drop的。最后查MSDN发现,IE需要dragenter
和dragover
都event.preventDefault()
才可以drop。 -
drop
的时候也有个小坑,在Firefox上drop
事件如果没有event.preventDefault()
,则drop后会发生跳转,然后就没有然后了。 - 最后关于
dragend
有个奇异的小坑,如果你使用的是Windows,那么很有可能你安装了一些诸如QQ电脑管家之类的工具,然后你的浏览器就很有可能被搜狗搜索之类的工具强行插入了,于是一个拖拽结束,立即会打开一个新窗口进行搜索,这时就需要把dragend
事件也event.preventDefault()
。
网友评论