HTML5提供了原生拖放功能的JavaScript API,使用起来很方便。
兼容性:
对于PC端浏览器,Firefox、Chrome、Safari支持良好,而IE和Edge浏览器有些特性不支持,如IE10和IE11、Edge对于dataTransfer.setData(format,data) ,只定义了"text"和"URL"两种有效的数据类型。而HTML5规范允许支持各种MIME类型。
详细参考这里: http://caniuse.com/#search=drag
本文实现HTML5原生拖放的应用Demo,用到了常用的方法和属性,兼容现代浏览器,还是先看效果:
下面详细介绍——
原生拖放事件:
应用于被拖动元素的事件:
- dragstart
按下鼠标并开始移动鼠标,会在被拖放的元素上触发dragstart事件。
注意:要使用HTML5的原生拖放功能,使该元素可拖动,需要设置draggable属性。默认情况下,图像、链接和被选中的文本是可以拖动的,因为它们的draggable属性已经自动被设置成true。
- drag
触发dragstart事件后,随即会触发drag事件,而且在元素被拖动期间会持续触发该事件。
- dragend
拖动停止(放开鼠标)的时候,会触发dragend事件。
应用于放置目标的事件:
- dragenter
只要有元素被拖动到放置目标上,就会触发dragenter事件。
- dragover
触发dragenter事件后,随即会触发dragover事件,而且只要被拖动元素在放置目标的范围内移动时,就会持续触发。
- dragleave
元素被拖出了放置目标,dragover事件不再发生,但会触发dragleave事件。
- drop
元素被放到了放置目标中,则会触发drop事件,而不是dragleave事件。
注意:(1)被拖动元素和放置目标可以设置为同一个元素,在自身上也可以触发drop事件,虽然好像没啥用 =。=
(2)被拖动元素进入放置目标范围和离开的参考标准是鼠标的位置,而不是鼠标下面拖动着的图像的边界
(3)拖动时显示在鼠标光标下方的图像,默认是该元素的一个副本,在dragstart事件中对完成对元素的复制(也可以通过setDragImage()自定义鼠标下拖动的元素),因此要隐藏本来的元素,最好在drag事件中处理,就是在复制后进行处理(参见文末的源代码)
dataTransfer对象
dataTransfer对象是事件对象的一个属性,只能在拖放事件的事件处理程序中访问。而且,dataTransfer对象的一些方法和属性也只能在特定的拖放事件中进行设置。
常用方法:
- setData(format,data)
在dragstart事件中,针对被拖放元素调用setData()函数,设置要传递的数据;用于从被拖放元素向放置目标传递字符串格式的数据。
第一个参数是数据类型,其中IE只定义了"text"和"URL"两种有效的数据类型;第二个参数是字符串,表示要传递的数据。
- getData(format)
在drop事件中,针对放置目标调用getData()函数,取得传递过来的数据。
第一个参数是setData( )中设置的数据类型
- setDragImage(element, x, y)
指定一副图像,当拖动发生的时候,显示在光标的下方。接受3个参数: 要显示的HTML元素和光标在图像中的x,y坐标。其中HTML元素可以是一幅图像,也可以是其他元素。
该属性IE10、IE11、Edge浏览器不支持。
- clearData(format)
清除以特定格式保存的数据。
常用属性:
- dropEffect
在dragenter事件中处理程序中,针对放置目标设置dropEffect属性的值,决定被拖动的元素能够执行哪种放置行为(同时被拖动元素拖到放置目标上时,会显示不一样的光标符号)
none:不能把拖动的元素放在这里。这是除了文本框之外所有元素默认的值。
move:应该把拖动的元素移动到放置目标。
copy:应该把拖动的元素复制到放置目标。
link:放置目标会打开拖动的元素(但拖动的元素必须是个链接,有URL地址)。
- effectAllowed
在dragstart事件处理程序中,针对被拖放元素设置effectAllowed属性的值,表示允许拖动元素的哪种dropEffect,和上面的dropEffect属性搭配使用。
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。
关于dataTransfer其他的一些方法和属性,以及更详细的介绍,请看这里 https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API
文末源码部分——
HTML代码:
<div id='container'> <div id='wrap'> <img src="http://d3.freep.cn/3tb_160909012718ljdh572240.jpg" title='鞋子'/> <img src="http://d2.freep.cn/3tb_160909012718973d572240.jpg" title='包子'/> <img src="http://d2.freep.cn/3tb_1609090127197ux5572240.jpg" title='薯片'/> </div> <div id='cart'></div> </div>
CSS代码:
*{ margin: 0; padding: 0; } body{ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } #wrap{ height: 100px; text-align: center; } img{ width: 100px; height: 100px; cursor: -webkit-grab; cursor: -moz-grab; cursor: grab; } #cart{ width: 500px; height: 100px; border-radius: 20px; margin: 50px auto 0; background-color: orange; } #cart.hover{ background-color: red; } #cart img{ width: 70px; height: 70px; margin: 15px; }
JS代码:
//被拖动元素的三个事件 function dragstart(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); e.dataTransfer.setData("text",target.title); //因为IE10、IE11和Edge不支持setDragImage()方法,需要判断 if(e.dataTransfer.setDragImage){ e.dataTransfer.setDragImage(target,50,50); } //effectAllowed事件和dropEffect事件搭配使用 e.dataTransfer.effectAllowed = 'move'; dragElement = target; } function drag(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); setOpacity(target,0); } function dragend(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); setOpacity(target,1); } //放置目标的四个事件 function dragenter(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); //重要!重写dragenter事件的默认行为,使其可以触发drop事件 EventUtil.preventDefault(e); //dropEffect事件和effectAllowed事件搭配使用 e.dataTransfer.dropEffect = 'move'; target.className = 'hover'; } function dragover(e){ e = EventUtil.getEvent(e); //重要!重写dragover事件的默认行为,使其可以触发drop事件 EventUtil.preventDefault(e); } function dragleave(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); target.className = ''; } function drop(e){ e = EventUtil.getEvent(e); var target = EventUtil.getTarget(e); var title = e.dataTransfer.getData("text"); console.warn('把%s添加到购物车中!',title); target.className = ''; dragElement.parentNode.removeChild(dragElement); var img = dragElement.cloneNode(); img.draggable = false; setOpacity(img,1); cart.appendChild(img); //重要!为了让Firefox支持正常的拖放,取消drop事件的默认行为 EventUtil.preventDefault(e); } //设置透明度 function setOpacity(element,value){ if(typeof element.style.opacity!='undefined'){ element.style.opacity=value; }else{ element.style.filter = "alpha(opacity="+value*100+")"; } } //事件处理,做兼容处理 var EventUtil={ //添加事件处理程序 addHandler:function(element,type,handler){ if(element.addEventListener){ element.addEventListener(type,handler,false); }else if(element.attachEvent){ element.attachEvent("on"+type,handler); }else{ element["on"+type]=handler; } }, //获取事件对象 getEvent:function(event){ return event?event:window.event; }, //获取事件的目标 getTarget:function(event){ return event.target||event.srcElement; }, //取消默认事件 preventDefault:function(event){ if(event.preventDefault){ event.preventDefault(); }else{ event.returnValue=false; } } }; var imgs = document.getElementsByTagName("img"), cart = document.getElementById('cart'), dragElement = null; for(var i=0; i<imgs.length; i++ ){ EventUtil.addHandler(imgs[i],'dragstart',dragstart); EventUtil.addHandler(imgs[i],'drag',drag); EventUtil.addHandler(imgs[i],'dragend',dragend); } EventUtil.addHandler(cart,'dragenter',dragenter); EventUtil.addHandler(cart,'dragover',dragover); EventUtil.addHandler(cart,'dragleave',dragleave); EventUtil.addHandler(cart,'drop',drop);
参考资料: 《JavaScript高级程序设计》,MDN