Javascript+HTML5 实现拖拽排序

正文

    对HTML5有过了解的童鞋一定会知道它可以说非常使用的一个新特性,就是出现了元素拖放的接口,具体的API想详细了解的建议直接w3school去了解.

    (1) 通过draggable属性使你的元素可拖拽.

    这里我会对用到东西一点点的做出声明以及解释,铺垫的差不多了开始上干货,先构建一下基础的html和css,因为本文主旨不是样式所以没搞得太美观,就是OSC的绿以及中国红为主色,别介意:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<! doctype  html>
< html >
< head >
   < meta  charset = "UTF-8" >
   < title >HTML5-Drag-Demo by 顽Shi</ title >
   < style >
     .column {
       height: 200px;
       width: 200px;
       float: left;
       border: 2px solid black;
       background-color: green;
       margin-right: 5px;
       text-align: center;
       cursor: move;
     }
     .column header {
       color: black;
       text-shadow: #000 0 1px;
       box-shadow: 5px;
       padding: 5px;
       background: red;
       border-bottom: 1px solid black;
     }
   </ style >
</ head >
< body >
   < div  id = "columns" >
     < div  draggable = "true" >< header >div1</ header ></ div >
     < div  draggable = "true" >< header >div2</ header ></ div >
     < div  draggable = "true" >< header >div3</ header ></ div >
   </ div >
</ body >
</ html >

    这就是html部分的代码,我们会逐渐完善它,效果图如下:

    html和css代码很简单我就不解释了,有疑问的请留言的说...这里我们只是为div加上了一个draggable的属性,并且将属性值设为true.这就是HTML5新增的一个属性,将一个元素设置为可拖拽.其实如果大家细心会发现浏览器上看到的很多内容都是可拖拽的,比如链接或者图片,我举一个百度首页的例子截图给大家,可以看到我拖拽百度logo也是可以的,而且它的代码上也没有draggables属性.

    这就是纯粹的浏览器支持,默认情况下大多数浏览器对于带有href属性的内容,比如超链接和图片等都是支持拖拽的,但是这个拖拽和我们今天要讲的DOM拖拽是不一样的.回到我们的demo,此时div1,div2,div3都可以进行拖拽了,类似与复制了一个自己的感觉,如果松开鼠标还会有一个滑回原来位置的默认动作.截图如下:

    拖拽动作在大多数浏览器下会创建一个自身的副本留在原来位置,这是一个很重要的东西,我们接下来会用到.

    (2) 对拖拽的动作进行监听

    现在元素可以移动了,但是距离我们的目标还有一定的差距,我们想做的是什么?

    我们希望的效果是元素被拖起来之后,可以在达到指定区域后,通过松开鼠标的动作将它固定在一个我们指定的位置上.所以我们要对这个过程中涉及到的动作加以监控.(不是所有的区域都能指定为元素被固定的区域,比如说图片就能接收其他元素).

    在拖动开始后我们希望被拖动的元素能变得透明一些,以区分出拖动和未拖动的不同,监听ondrugstart可以轻松实现,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
<script>
   var  columns = document.querySelectorAll( '#columns .column' );
   
   [].forEach.call(columns, function (column){
     column.addEventListener( "dragstart" ,domdrugstart, false );
   });
   
   function  domdrugstart(e) {
     e.target.style.opacity =  '0.5' ;
   }
</script>

    此时拖动一个元素,它的透明度会变为50%.不过拖动结束后这一效果还保留着,所以要在拖动结束重置会100%.这个留到最后实现即可.

    接下来我们实现的一个效果,是要在拖拽时将其可能放置的位置标识出来,显示出哪里是可能放置的位置,尤其在鼠标悬停或者划过的时候.这一过程需要ondragenter,ondragover以及ondragleave这三个事件的监听.代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
     .column.over {
       border: 3px dashed  #000;
     }
     //将这个样式添加到style中
     
     <script>
       var  columns = document.querySelectorAll( '#columns .column' );
       
       [].forEach.call(columns, function (column){
         column.addEventListener( "dragstart" ,domdrugstart, false );
         column.addEventListener( 'dragenter' , domdrugenter,  false );
         column.addEventListener( 'dragover' , domdrugover,  false );
         column.addEventListener( 'dragleave' , domdrugleave,  false );
       });
       
       function  domdrugstart(e) {
         e.target.style.opacity =  '0.5' ;
       }
       function  domdrugenter(e) {
         e.target.classList.add( 'over' );
       }
       function  domdrugover(e) {
         if  (e.preventDefault) {
           e.preventDefault(); 
         }
     
         e.dataTransfer.dropEffect =  'move' ;
     
         return  false ;
       }
       function  domdrugleave(e) {
         e.target.classList.remove( 'over' ); 
       }      
     </script>

    解释一下代码,dragenter事件的动作是拖动后鼠标进入另一个可接受区域,此时给添加了监听的对象加上一个虚线的边框,之所以使用dragenter而不是dragover是因为前者只在进入区域时触发一次,后者会反复触发.dragover则是为了阻止一些类似超链接拖动跳转的默认动作,dragleave则是去掉当拖拽鼠标划出区域时,去掉虚线.

    (3)  添加释放动作

    到此为止元素已经可以进行拖拽,并且并且在拖拽的过程有虚线的提示.但是现在还缺少一个完成的动作,也就是拖拽结束的动作,这个动作要有两个作用,一是和over类似去阻止浏览器的默认动作,二是根据我们的需求进行DOM操作.

    这里用到drop以及dragend:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<script>
   var  columns = document.querySelectorAll( '#columns .column' );
   
   [].forEach.call(columns, function (column){
     column.addEventListener( "dragstart" ,domdrugstart, false );
     column.addEventListener( 'dragenter' , domdrugenter,  false );
     column.addEventListener( 'dragover' , domdrugover,  false );
     column.addEventListener( 'dragleave' , domdrugleave,  false );
     column.addEventListener( 'drop' , domdrop,  false );
     column.addEventListener( 'dragend' , domdrapend,  false );    
   });
   
   function  domdrugstart(e) {
     e.target.style.opacity =  '0.5' ;
   }
   function  domdrugenter(e) {
     e.target.classList.add( 'over' );
   }
   function  domdrugover(e) {
     if  (e.preventDefault) {
       e.preventDefault(); 
     }
 
     e.dataTransfer.dropEffect =  'move' ;
 
     return  false ;
   }
   function  domdrugleave(e) {
     e.target.classList.remove( 'over' ); 
   }   
   function  domdrop(e) {
     if  (e.stopPropagation) {
       e.stopPropagation();
     }
     return  false ;
   }
   function  domdrapend(e) {
     [].forEach.call(columns,  function  (column) {
       column.classList.remove( 'over' );
        column.style.opacity =  '1' ;
     });
   }     
</script>

    至此我们会发现,还没有完成元素的交换这一动作,只是让所有的元素看起来可拖拽了而已,因为还缺少最后的DataTransfer对象.这是一个很神奇的东西,它使拖动的过程中可以发送数据,可以在dragstart中设置并且在drop或者dragend时读取.

    这里我们先看添加的代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   var  dragEl =  null ;
 
   function  domdrugstart(e) {
     e.target.style.opacity =  '0.5' ;
     
     dragEl =  this ;
     
     e.dataTransfer.effectAllowed =  "move" ;
     e.dataTransfer.setData( "text/html" , this .innerHTML);
   }
   
   function  domdrop(e) {
     if  (e.stopPropagation) {
       e.stopPropagation();
     }
     
     if  (dragEl !=  this ) {
       dragEl.innerHTML =  this .innerHTML;
       this .innerHTML = e.dataTransfer.getData( 'text/html' );
     }    
     
     return  false ;
   }

    效果如图:

    最后这段代码可能大家会有疑惑,解释一下.首先定义了一个全局变量dragEl它的作用就是用来存储被拖拽元素的html,然后在释放拖拽时和释放区域的元素进行交换.e.dataTransfer.effectAllowed这个是用来限制元素的,它限制了元素的拖动类型为move.紧接着通过代码将被拖动元素的html进行setData.然后这里set的值可以通过get进行获取.

    基本上到这就完成了整个功能,稍微进行一下封装就可以形成插件,这个有兴趣的可以自己搞~最后附源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<! doctype  html>
< html >
< head >
   < meta  charset = "UTF-8" >
   < title >HTML5-Drag-Demo by 顽Shi</ title >
   < style >
     .column {
       height: 200px;
       width: 200px;
       float: left;
       border: 1px solid black;
       background-color: green;
       margin-right: 5px;
       text-align: center;
       cursor: move;
     }
     .column header {
       color: black;
       text-shadow: #000 0 1px;
       box-shadow: 5px;
       padding: 5px;
       background: red;
       border-bottom: 1px solid black;
     }
     .column.over {
       border: 3px dashed #000;
     }
   </ style >
</ head >
< body >
   < div  id = "columns" >
     < div  class = "column"  draggable = "true" >< header >div1</ header ></ div >
     < div  class = "column"  draggable = "true" >< header >div2</ header ></ div >
     < div  class = "column"  draggable = "true" >< header >div3</ header ></ div >
   </ div >
</ body >
< script >
   var columns = document.querySelectorAll('#columns .column');
   
   var dragEl = null;
   
   [].forEach.call(columns,function(column){
     column.addEventListener("dragstart",domdrugstart,false);
     column.addEventListener('dragenter', domdrugenter, false);
     column.addEventListener('dragover', domdrugover, false);
     column.addEventListener('dragleave', domdrugleave, false);
     column.addEventListener('drop', domdrop, false);
     column.addEventListener('dragend', domdrapend, false);    
   });
   
   function domdrugstart(e) {
     e.target.style.opacity = '0.5';
     
     dragEl = this;
     
     e.dataTransfer.effectAllowed = "move";
     e.dataTransfer.setData("text/html",this.innerHTML);
   }
   function domdrugenter(e) {
     e.target.classList.add('over');
   }
   function domdrugover(e) {
     if (e.preventDefault) {
       e.preventDefault(); 
     }
 
     e.dataTransfer.dropEffect = 'move';
 
     return false;
   }
   function domdrugleave(e) {
     e.target.classList.remove('over'); 
   }   
   function domdrop(e) {
     if (e.stopPropagation) {
       e.stopPropagation();
     }
     
// 位置互换
     if (dragEl != this) {
      dragEl.innerHTML = this.innerHTML;
      this.innerHTML = e.dataTransfer.getData('text/html');
    }    
     
     return false;
   }
   function domdrapend(e) {
     [].forEach.call(columns, function (column) {
       column.classList.remove('over');
        column.style.opacity = '1';
     });
   }     
</ script >
</ html >
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值