首页 > 代码库 > 运用HTML5原生拖动事件(drag)实现拖动效果

运用HTML5原生拖动事件(drag)实现拖动效果

  拖动效果相信很多朋友都自己写过,不管你用原生JS还是Jquery要实现起来也很简单,但是今天我想介绍的是运用HTML5标准中定义的原生拖动事件实现拖动效果。

  一、背景:

    其实说是HTML5标准定义,其实最早在IE4中就有拖放功能的API,只是在IE4中,网页中只有两种对象可以拖放:图像和某些文本。并且在IE4中唯一有效的放置目标是文本框。到了IE5.5,拖放功能得到了扩展,让网页中的任何元素都可以拖放。最终HTML5以IE的实力为基础制定了拖放规范。FF3.5+,Safari3+和Chrome根据规范实现了原生拖放。

  二、事件:

    原生拖放中涉及到的事件分为分下列两个阶段:

     (1)拖动某元素时,该阶段会依次触发下列事件(该阶段的事件目标--即target或srcElement都是这个被拖动元素):  

         1>dragstart——鼠标移入目标元素并且按下左键触发。

         2>drag——dragstart触发后移动鼠标连续触发该事件(类似mousemove事件)

         3>dragend——拖动停止时触发(无论此时拖动元素在有效位置还是无效位置)。

      (2)当元素被拖动到一个有效的放置目标上时,触发下列事件(该阶段的事件目标--即target或srcElement都是这个目标元素):

         1>dragenter——只要有元素被拖动到放置目标上,就触发dragenter事件(类似mouseover)

         2>dragover——触发dragenter后在有效目标范围内移动时连续触发该事件

         3>dragleave——被拖动元素从目标范围内被拖出到目标范围外时触发

         4>drop——被拖动元素被放到了目标范围内(即在有效目标范围内松口鼠标左键)

            注意:这里dragleave和drop二者只能触发其一!

  三、定义放置位置:

     虽然所有元素都支持放置目标事件,但是这些元素默认情况下是不允许放置的,如果拖动元素经过不允许放置的元素,那无论怎样都无法触发drop事件。所    以我们必须先定义出放置的目标元素。

     定义目标元素也很简单,只要阻止dragenter和dragover事件的默认行为就可以了,假设我们的目标元素为一个id为drag-area的div,我们要把它定义成我    们的目标元素,只需下面代码就可以了:

 drag_area.ondragover=function(evt){     //阻止dragover的默认事件        var evt=evt || window.event;        if(typeof evt.preventDefault=="function"){            evt.preventDefault();        }else{            evt.returnValue=false;        }    }    drag_area.ondragenter=function(evt){   //阻止dragenter的默认事件        var evt=evt || window.event;        if(typeof evt.preventDefault=="function"){            evt.preventDefault();        }else{            evt.returnValue=false;        }    }

 

    (有小伙伴可能会问了,啊~阻止默认事件不是可以直接用return false么,干嘛这么麻烦。的确,return false可以阻止默认事件和事件传播,而且我测试过,用return false;的确也可以达到相同效果。这里主要是firefox的一个问题,我的firefox是最新版本,直接用return false没有问题,但是据 http://blog.csdn.net/goldlevi/article/details/5721348  说,稍早一些的firefox版本如果dragenter和dragover没有设置preventDefault的话不会在该元素上触发drag事件的,偷个懒,就不自己测试其他firefox了~关于FF还有一点问题,后面会提到);

四.实现

  前面解释了一下自定义放置目标,这里具体的实现就不多说了(其实原理和普通实现差不多~)直接上代码:

<!DOCTYPE html><html><head>    <meta http-equiv="content-type" content="text/html,charset=utf-8">    <title></title>    <style type="text/css">        #drag-area{            width: 500px;height:500px;            border: 1px solid red;            position: relative;        }        #my-img{            position: absolute;        }    </style></head><body>    <div id="drag-area">        <img src="http://www.mamicode.com/pity.gif"  id="my-img">    </div></body><script type="text/javascript">    var my_img=document.getElementById("my-img");    var drag_area=document.getElementById("drag-area");    my_img.ondragstart=function(evt){       //在被拖动的元素的dragstart事件中取得坐标便宜        var evt=evt || window.event;        //即代码中evt.clientX-this.offsetLeft和evt.clientY-this.offsetTop,并保存在dataTransfer对象中        evt.dataTransfer.setData("text",(evt.clientX-this.offsetLeft)+";"+(evt.clientY-this.offsetTop));    }    drag_area.ondragover=function(evt){     //阻止dragover的默认事件        var evt=evt || window.event;        if(typeof evt.preventDefault=="function"){            evt.preventDefault();        }else{            evt.returnValue=false;        }    }    drag_area.ondragenter=function(evt){   //阻止dragenter的默认事件        var evt=evt || window.event;        if(typeof evt.preventDefault=="function"){            evt.preventDefault();        }else{            evt.returnValue=false;        }    }    drag_area.ondrop=function(evt){        var evt=evt || window.event;        var drag_data=http://www.mamicode.com/evt.dataTransfer.getData("Text").split(";");//从dataTransfer对象中取出数据,并将字符串分割成数组        var offset_x=drag_data[0],//取得横向偏移                offset_y=drag_data[1];//取得纵向偏移        if(typeof evt.preventDefault=="function"){ //阻止drop事件的默认行为            evt.preventDefault();        }else{            evt.returnValue=false;        }       my_img.style.left=(evt.clientX-offset_x)+"px";//给拖动元素的left,top赋值        my_img.style.top=(evt.clientY-offset_y)+"px";    }</script></html>

   代码限定了被拖动元素(my_img)只能在(drag_area)这个500*500的div里面拖动。代码中我们在拖放元素的dragstart事件中取得了鼠标点击时的坐标偏移evt.clientX-this.offsetLeft和evt.clientY-this.offsetTop然后将其存放在dataTransfer对象中,

然后又在目标元素的drop事件里从dataTransfer对象中取出了偏移值,用来计算拖动元素的left和top。

   解释下dataTransfer这个对象。该对象是事件对象的一个属性,通常我们在利用它在被拖动元素(如代码中的my_img)dragstart事件处理程序中设置它的值,在目标元素(代码中的drag_area)drop事件(并且只能在drop事件)中读取其值,此外,浏览器在我们拖动文本(图片/链接)时,会自动将拖动的文本(url)存放在该对象中。

    设置dataTransfer内容的语法如下:

          event.dataTransfer.setData("数据类型","值")——这里数据类型虽然HTML定义了多种MIME类型,但是最好根据情况只使用

                                   ‘text’—保存字符串和’URL‘—保存url(ie只定义了这两种类型);

                                    值当然就是你需要的值啦。

    通过dataTransfer对象提取数据语法如下:

          event.dataTransfer.getData("数据类型")——这里数据类型同setData中的数据类型。

    另外dataTransfer对象还有两个属性,dropEffect和EffectAllowed。不过个人感觉很鸡肋,就不多说了,只是改了下鼠标的样式有木有啊~

  好了,有兴趣的同学把图片链接换一下试试吧~该方法兼容性尚可:opera不支持没话说,在IE6+,chrome中都没问题,但是在FF中拖动图片会自动打开图片的url,而且阻止不了(代码中drop事件有阻止默认代码~测试版本ff31.0和ff32.03)=_=!根本不科学嘛~希望看到本文,有解决方法的大神留言。

  总结一下,虽然我们用mousedown、mousemove、mouseup也能方便做出拖放效果,而且没有上面说的兼容性和FF中的BUG,但是如果项目不需要兼容opera,而且是拖拽选中文本的话,此方法就比较方便了(上面说过,拖拽文本浏览器会自动将选中并拖拽的文本、URL存放在dataTransfer对象中)。而且,做前端不就是折腾么。。。。

 

运用HTML5原生拖动事件(drag)实现拖动效果