页面拖拽元素原理及实现代码

2018-02-03 10:27:54来源:http://570109268.iteye.com作者:青春..荒唐人点击

分享

今天总结下常见的拖拽效果的实现原理及场景应用,拖拽功能主要是用在让用户做一些自定义的动作,比如拖动排序,弹出框拖动移动等等,效果还是蛮不错的

 

现在有多种实现方法,常用的有H5的拖放API和原生JS两种方法。

(1)HTML5提供专门的拖拽与拖放的API,在不考虑兼容的情况下,以后实现这类效果就不必乱折腾了

【兼容:IE9+和其他主流浏览器均支持,而IE8和早前版本均不支持draggable属性】

(2)原生JS实现

接下来我分开解释下

 

(1)H5的draggable属性实现

首先说下HTML5里新增的draggable属性

在html5中,默认<img.../>和设置了href的<a>是可拖动的,其他都需手动把元素的draggable属性设置为true

然后使元素携带数据应该为被拖动元素的ondragstart拖拽开始(开始拖动触发该事件)事件指定监听器,在监听器中让拖动操作可以携带数据。

之后为了让document接受放的动作,为documen的ondragover(拖拽结束)事件设定监听器,在监听器中取消document对拖动事件的默认行为。

然而,不同浏览器在元素拖到指定位置释放之后默认的动作是不同的(firefox释放后会跳转到新页面,chrome则没有任何动作),所以我们要取消拖放操作的默认动作,为document的ondrop绑定监听器

 

【简答通俗讲】就是:

H5允许对页面元素进行拖动,只要在元素的属性中加上draggable="true",就可以拖动了。

在拖动的同时,必须记录被拖动的元素,通过在元素上注册事件ondragstart即可实现,比如规定了一个img元素可以拖动,代码如下:

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>元素拖拽</title></head><body><img src="./01.jpg" draggble="true"  ondragstart="drag(event);"><script>    function drag(event) {        event.dataTransfer.setData("Text",event.target.id);        console.log(event.target.id)    }</script></body></html>

在拖动img的时候,drag函数就会把img的id属性记录在事件中,记录值的类型是Text。

【下一步】是规定哪些元素可以放置被拖动的img,比如一个div可以放置这个被拖动的img, 需要在这个div上定义一个事件:ondragover(被拖动元素进入本元素的范围内拖动时不断触发)。在这个事件中,需要阻止对事件的默认处理方式,其实就是硬性的加上一句:event.preventDefault()

<style type="text/css">    .main{        width: 200px;        height: 300px;        border: 1px solid red;    }</style><div  ondragover="allowDrop(event);"></div>    function allowDrop(event) {        alert("进入范围")        event.preventDefault();    }

 这里通过运用ondragover触发事件,我们只要将图片拖拽到框里,便会提示已经入范围框

【下一步】要在容纳这个img的div中把这个img放到里面,当放置的时候,会触发ondrop事件(其他元素被放到了本元素时触发)。

(1)通过event.dataTransfer.getData("Text")来得到上面event.dataTransfer.setData("Text",ev.target.id)的img的id

(2)通过event.target.appendChild(document.getElementById(data))来把img放到div中,注意这里event.target指的是div,不是上面的img了。至此,页面的拖动完成了

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>元素拖拽</title>    <style type="text/css">        .main{            width: 300px;            height: 300px;            border: 1px solid red;            position: absolute;            top: 300px;            left: 300px;        }    </style></head><body><img src="./01.jpg" draggble="true"  ondragstart="drag(event);"><div  ondragover="allowDrop(event);" ondrop="drop(event)"></div><script>    function drag(event) {        event.dataTransfer.setData("Text",event.target.id);        console.log(event.target.id)    }    function allowDrop(event) {        console.log("进入范围")        event.preventDefault();    }    function drop(event) {        console.log("鼠标已松开")        event.preventDefault();        var data=event.dataTransfer.getData("Text");        console.log(event.target)        event.target.appendChild(document.getElementById(data));    }</script></body></html>

 

【W3实例讲解:】

 

<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>拖放Demo</title>    <style type="text/css">        #div1 {            width: 198px;            height: 66px;            padding: 10px;            border: 1px solid #aaaaaa;        }    </style>    <script type="text/javascript">        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));        }    </script></head><body><p>请把 W3School 的图片拖放到矩形中:</p><div  ondrop="drop(event)" ondragover="allowDrop(event)"></div><br /><img  src="/2014th7cj/d/file/p/20180201/odkddtcq3wk.gif" draggable="true" ondragstart="drag(event)" /></body></html>
 下面研究一下拖放事件的所有不同部分

 

 ①把元素设置为可拖放

首先:为了把一个元素设置为可拖放,请把 draggable 属性设置为 true:

 

<img draggable="true">
 ②拖放的内容 - ondragstart 和 setData()

 

然后,规定当元素被拖动时发生的事情。在上面的例子中,ondragstart 属性调用了一个 drag(event) 函数,规定拖动什么数据。

dataTransfer.setData() 方法设置被拖动数据的数据类型和值:

 

function drag(ev) {    ev.dataTransfer.setData("text", ev.target.id);}

 

在本例中,数据类型是 "text",而值是这个可拖动元素的 id ("drag1")

③拖到何处 - ondragover

ondragover 事件规定被拖动的数据能够被放置到何处

默认地,数据/元素无法被放置到其他元素中。为了实现拖放,我们必须阻止元素的这种默认的处理方式。

这个任务由 ondragover 事件的 event.preventDefault() 方法完成:

 

event.preventDefault()
 ④进行放置 - ondrop

 

当放开被拖数据时,会发生 drop 事件。

在上面的例子中,ondrop 属性调用了一个函数,drop(event):

 

function drop(ev) {    ev.preventDefault();    var data = ev.dataTransfer.getData("text");    ev.target.appendChild(document.getElementById(data));}

 

【代码解释:】

 1. 调用 preventDefault() 来阻止数据的浏览器默认处理方式(drop 事件的默认行为是以链接形式打开)

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

 3 .被拖数据是被拖元素的 id ("drag1")

 4 .把被拖元素追加到放置元素中

 

 

【更多实例】

来回拖放图片:在两个 <div> 元素之间来回拖放图像

 
<!DOCTYPE html><html><head>    <meta charset="UTF-8">    <title>拖放Demo</title>    <style type="text/css">        #div1, #div2 {            float: left;            width: 198px;            height: 66px;            margin: 10px;            padding: 10px;            border: 1px solid #aaaaaa;        }    </style>    <script type="text/javascript">        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));        }    </script></head><body><div  ondrop="drop(event)" ondragover="allowDrop(event)">    <img src="/2014th7cj/d/file/p/20180201/odkddtcq3wk.gif" draggable="true" ondragstart="drag(event)"         /></div><div  ondrop="drop(event)" ondragover="allowDrop(event)"></div></body></html>

 

 

这里我再列个W3的例子:

<!DOCTYPE html><html >    <head>        <meta charset="utf-8">        <title>HTML5-draggable(拖放)</title>        <style type="text/css">            #div1, #div2 {float:left; width:100px; height:35px;              margin:10px;padding:10px;border:1px solid #aaaaaa;}        </style>        <script src="js/modernizr.js" type="text/javascript" charset="utf-8"></script>        <script type="text/javascript">            /*             * 虽然已经设定了img元素可被拖动,但是浏览器默认地,             无法将数据/元素放置到其他元素中。             * 如果有需要设置某些元素可接受被拖动元素,则要阻止它的默认行为,             * 这要通过设置该接收元素的ondragover 事件,调用event.preventDefault() 方法             */            function allowDrop(ev) {                ev.preventDefault(); //阻止默认行为                                //ev.target.id                //此处ev.target是接收元素,通过事件被绑定在哪个元素即可区分            }            /*             * 当该img元素被拖动时,会触发一个ondragstart 事件,               该事件调用了一个方法drag(event)。             */            function drag(ev) {                //ev.dataTransfer.setData() 方法设置被拖数据的数据类型(Text)和                //值(被拖元素id),                //该方法将被拖动元素的id存储到事件的dataTransfer对象内,                //ev.dataTransfer.getData()可将该元素取出。                //此处ev.target是被拖动元素                ev.dataTransfer.setData("Text", ev.target.id);             }            /*             * 当被拖元素移动到接收元素,             * 松开鼠标时(即被拖元素放置在接收元素内时)会出发ondrop事件             */            function drop(ev) {                ev.preventDefault(); //阻止默认行为                var data = ev.dataTransfer.getData("Text"); //将被拖动元素id取出                ev.target.appendChild(document.getElementById(data));                 //将被拖动元素添加到接收元素尾部            }        </script>    </head>    <body>        <div  ondrop="drop(event)" ondragover="allowDrop(event)">            <!--为了使元素可拖动,把 draggable 属性设置为 true-->             <img src="http://www.w3school.com.cn/i/w3school_logo_black.gif"                  draggable="true" ondragstart="drag(event)"  />        </div>                <div  ondrop="drop(event)" ondragover="allowDrop(event)"></div>    </body></html>

  

 

接下来举个简单随意拖放例子:

<!DOCTYPE HTML><html><head>    <meta http-equiv="Content-Type" content="text/html" charset="utf-8"/>    <title>可拖动</title>    <style type="text/css">        #source{            border: 1px solid #000000;            background-color: #ccc;            width: 80px;            height: 80px;        }    </style></head><body><div >    HELLO WORLD。</div><script type="text/javascript">    var source = document.getElementById("source");    source.ondragstart = function(evt){//ondragstart:开始拖动时触发        //让拖动携带数据        console.log(evt)        evt.dataTransfer.setData("text/plain","www.fkjava.org");    }    document.ondragover = function(evt){     //ondragover:被拖动元素进入本元素的范围内拖动时不断触发        //取消事件的默认行为        return false;    }    document.ondrop = function(evt){//ondrop:其他元素被放到了本元素时触发        source.style.position = "absolute";        source.style.left = evt.pageX + "px";        source.style.top = evt.pageY + "px";        //取消事件的默认行为        return false;    }</script></body></html>

附:触发事件

ondragstart:开始拖动时触发

ondrag:拖动过程中不断触发

ondragend:拖动结束时触发

ondragenter:被拖动元素进入本元素的范围内时触发

ondragover:被拖动元素进入本元素的范围内拖动时不断触发

ondragleave:被拖动元素离开本元素时触发

ondrop:其他元素被放到了本元素时触发

 

【再来说下DataTransfer对象】

拖放触发的拖放事件有一个dataTransfer的属性,该属性值是一个DataTransfer对象(可以去chrome浏览器

控制台打印验证,这里我已经打印出来,打开控制台找打即可)

dataTransfer对象:提供了对于预定义的剪贴板格式的访问,以便在拖曳操作中使用。

通俗的讲就是在拖曳操作的过程中,我们可以用过dataTransfer对象来传输数据,以便在拖曳操作结束的时候对数据进行其他的操作。

 

【对象属性】

(1)dropEffect:设置或返回拖放目标上允许发生的拖放行为。如果此处设置的拖放行为不再effectAllowed属性设置的多种拖放行为之内,拖放操作将会失败。该属性值只允许为“null”、“copy”、“link”和“move”四值之一。

(2)effectAllowed:设置或返回被拖动元素允许发生的拖动行为。该属性值可设为“none”、“copy”、“copyLink”、“copyMove”、“link”、“linkMove”、“move”、“all”和“uninitialized”。

(3)items:该属性返回DataTransferItems对象,该对象代表了拖动数据。

(4)types:该属性返回一个DOMStringList对象,该对象包括了存入dataTransfer中数据的所有类型。

 

【对象方法】

(1)setData(format,data):将指定格式的数据赋值给dataTransfer对象,参数format定义数据的格式也就是数据的类型,data为待赋值的数据

(2)getData(format):从dataTransfer对象中获取指定格式的数据,format代表数据格式,data为数据。

(3)clearData([format]):从dataTransfer对象中删除指定格式的数据,参数可选,若不给出,则为删除对象中所有的数据。

(4)addElement(element):添加自定义图标

(5)setDragImage(element,x,y):设置拖放操作的自定义图标。其中element设置自定义图标,x设置图标与鼠标在水平方向上的距离,y设置图标与鼠标在垂直方向上的距离。

下面展示一个允许通过拖动来添加,删除”收藏夹“功能

 

 

 

(2)原生JS实现

一、拖拽的流程动作

①鼠标按下

②鼠标移动

③鼠标松开

二、拖拽流程中对应的JS事件

①鼠标按下会触发onmousedown事件

obj.onmousedown = function(e) {        //..........  } 

 ②鼠标移动会触发onmousemove事件

obj.onmousemove = function(e) {      //......  }

 ③鼠标松开会触发onmouseup事件

obj.onmouseup = function() {       //......  }

 三、实现的原理讲解

拖拽其实是通过获取鼠标移动的距离来实现的,即计算移动前的位置的坐标(x,y)与移动中的位置的坐标(x,y)差值。

当鼠标按下或鼠标移动时,都可以获取到当前鼠标的位置,即移动前的位置与移动中的位置。

那么上面①与②的代码就应该变成这样

var mouseDownX,mouseDownY  // 因在移动中需计算鼠标的偏移需要用到鼠标按下时的坐标,固声明称全局变量  obj.onmousedown = function(e) {      mouseDownX = e.pageX;      mouseDownY = e.pageY;  }  obj.onmousemove = function(e) {      var mouseMoveX = e.pageX,mouseMoveY = e.pageY;  } 

 移动前与移动后坐标有了,那么计算偏移,先看下图

 

很明显移动后元素

X坐标为  鼠标移动后的X坐标 - 鼠标按下的X坐标 + 元素的初始X坐标

Y坐标为  鼠标移动后的Y坐标 - 鼠标按下的Y坐标 + 元素的初始Y坐标

把新的 X,Y 替换元素的 X,Y 就搞定了

那么代码就应该更换为:

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>元素拖拽</title>    <style type="text/css">        #link{            position: absolute;        }    </style></head><body><p id='link'>我是可以拖拽的</p><script type="javascript">    var obj = document.getElementById('link');    var mouseDownX,mouseDownY,initX,initY,flag = false;      // 因在移动中需计算鼠标的偏移需要用到鼠标按下时的坐标,固声明称全局变量    obj.onmousedown = function(e) {    // e为鼠标事件对象,属性pageX为鼠标X坐标,pageY为鼠标Y坐标        //鼠标按下时所在的X,Y坐标        mouseDownX = e.pageX;        mouseDownY = e.pageY;        //初始位置的X,Y坐标        initX = obj.offsetLeft;        initY = obj.offsetTop;        //鼠标被按下        flag = true;    }    obj.onmousemove = function(e) {       if(flag){//确认鼠标已按下           var mouseMoveX = e.pageX,mouseMoveY = e.pageY;           this.style.left =                   parseInt(mouseMoveX) - parseInt(mouseDownX) + parseInt(initX) + "px";           this.style.top =                   parseInt(mouseMoveY) - parseInt(mouseDownY) + parseInt(initY) + "px";       }else{//未按下鼠标}    }    obj.onmouseup = function() {        //标识已松开鼠标        flag = false;    }</script></body></html>

 经测试,拖动速度过快时会出现明显卡顿,这个之后再讨论性能优化

需要注意的事:

①如果用jquery库来写的话三个事件为mousedown、mousemove、mouseup,名称稍微有点差别。

②还有一点,被拖拽的元素的样式要设置成绝对或相对位置才有效果。

至此,最简单的元素拖拽功能就讲完了~~~~

如有不正确之处欢迎大家指正!

 

 

 

后续补充下,这里我们讨论下代码的优化:

<!DOCTYPE html>  <html>  <head lang="en">      <meta charset="UTF-8">      <title>元素拖拽</title>      <style type="text/css">          #xixi {              width:200px; height: 200px; position:absolute;              left: 50px; top: 50px; background-color: lightcyan;          }          #haha {              position:absolute; left:300px; top:300px;              background-color: yellow; width:200px; height: 200px;          }        </style>      <script type="text/javascript" src="js/mylib.js"></script>      <script type="text/javascript">          window.onload = function() {              var obj1 = createDraggableObject();              var obj2 = createDraggableObject();              obj1.init($('xixi'));              obj2.init($('haha'));          };      </script>    </head>  <body>      <div >xixi!</div>      <div >haha!</div>  </body>  </html>  

 外部JavaScript文件代码如下所示:

/**  * 根据id获取页面元素  * @param id  * @returns {HTMLElement}  */  function $(id) {      return document.getElementById(id);  }    /**  * 创建可拖拽对象的工厂方法  */  function createDraggableObject() {      return {          obj: null, left: 0, top: 0,          oldX: 0, oldY: 0, isMouseLeftButtonDown: false,          init: function (obj) {              this.obj = obj;              var that = this;              this.obj.onmousedown = function (args) {                  var evt = args || event;                  this.style.zIndex = 100;                  that.isMouseLeftButtonDown = true;                  that.oldX = evt.clientX;                  that.oldY = evt.clientY;                  if (this.currentStyle) {                      that.left = parseInt(this.currentStyle.left);                      that.top = parseInt(this.currentStyle.top);                  }                  else {                      var divStyle = document.defaultView.getComputedStyle(this, null);                      that.left = parseInt(divStyle.left);                      that.top = parseInt(divStyle.top);                  }              };              this.obj.onmousemove = function (args) {                  that.move(args || event);              };              this.obj.onmouseup = function () {                  that.isMouseLeftButtonDown = false;                  this.style.zIndex = 0;              };          },          move: function (evt) {              if (this.isMouseLeftButtonDown) {                  var dx = parseInt(evt.clientX - this.oldX);                  var dy = parseInt(evt.clientY - this.oldY);                  this.obj.style.left = (this.left + dx) + 'px';                  this.obj.style.top = (this.top + dy) + 'px';              }          }      };  }  

 

 最后展示一个实际应用:通过H5的draggable的APL实现----删除和收藏夹功能

<!DOCTYPE html><html><head>    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>    <title>通过拖放实现添加、删除</title>    <style type="text/css">        div>div{            display:inline-block;            padding: 10px;            background-color: #aaa;            margin: 3px;        }    </style></head><body><div>    <h2>可将喜欢的项目拖到收藏夹</h2>    <div draggable="true" ondragstart="dsHandler(event);">勿忘心安</div>    <div draggable="true" ondragstart="dsHandler(event);">照顾自己</div>    <div draggable="true" ondragstart="dsHandler(event);">Number 9</div>    <div draggable="true" ondragstart="dsHandler(event);">崇拜</div></div><div >    <h2 ondragleave="return false;">收藏夹</h2></div><div  draggable="false">我是垃圾桶</div><script type="text/javascript">    var dest = document.getElementById("dest");    var dsHandler = function (evt){        evt.dataTransfer.setData("text/plain","<item>"+evt.target.innerHTML);    }    dest.ondrop = function(evt){        var text = evt.dataTransfer.getData("text/plain");        if(text.indexOf("<item>") == 0){            var newEle = document.createElement("div");            newEle.id = new Date().getUTCMilliseconds();            newEle.innerHTML = text.substring(6);            newEle.draggable = "true";            newEle.ondragstart = function(evt){                evt.dataTransfer.setData("text/plain","<remove>"+newEle.id);            }            dest.appendChild(newEle);        }    }    document.getElementById("gb").ondrop = function(evt){        var id = evt.dataTransfer.getData("text/plain");        if(id.indexOf("<remove>") == 0){            var target = document.getElementById(id.substring(8));            dest.removeChild(target);        }    }    document.ondragover = function(evt){        return false;    }    document.ondrop = function(evt){        return false;    }</script></body></html>

 

 

 

 

 

.

 

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台