当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 基于JQuery的列表拖动排序实现代码

基于JQuery的列表拖动排序实现代码

2018年09月17日  | 移动技术网IT编程  | 我要评论
要求 拖动排序,从名字就不难想像,就是按住一行数据拖到想要的排序位置,保存新的排序队列。 思路 首先给列表行建立锚点,绑定mousedown和mouseup事件,当鼠标移动到想要插入的位置时,将对象

要求

拖动排序,从名字就不难想像,就是按住一行数据拖到想要的排序位置,保存新的排序队列。

思路

首先给列表行建立锚点,绑定mousedown和mouseup事件,当鼠标移动到想要插入的位置时,将对象行移动到目标行,然后对其经过的所有行进行排序处理。

思路很简单,但这里面仍然有几个问题要注意

1、移动到什么位置可以视作要插入到目标行的位置。
2、移动出了顶端和底端时,判断为第一和最后。
3、向上移动和向下移动的处理

解决

关于事件

javascript里鼠标按下和放开事件为onmousedown,onmouseup,jquery里是mousedown,mouseup,所以,这里使用mousedown和mouseup

首先,要知道界面有多少行,每一行有多高,因为要判断鼠标的移动距离
 

. 代码如下:


var tbodyheight=setting.frame.outerheight();  //setting.frame,父对象
var linenum=$("."+setting.dgline).length;  //setting.dgline,每一行的classname
var lineheight=math.ceil(tbodyheight/linenum);

 

之所有要取linenum(行数),除了计算行高外,还有个目的是要使用index(),通过序列索引值来进行移动行到目标位置

当mousedown事件触发后,就要开始计算鼠标移动的距离,用于判断该行到底要移动到什么位置

 

. 代码如下:


dgid=$(this).attr(setting.id);  //移动行的id,setting.id,是每一行用来标记id的名称
thisindex=$("#"+setting.linepre+dgid).index(); //该行的索引,setting.linepre,每一行id关辍
thislinetop=$("#"+setting.linepre+dgid).offset().top; //该行的top值
topdistance=thisindex*lineheight;  //该行距离第一行顶端的距离
downdistance=(linenum-thisindex-1)*lineheight; //该行距离最后一行底端的距离

 


dgid主要是用来区分每一行的标识,一般的列表都是程序循环输出来的,如果没有这样一个id,就分不出哪行是哪行,所以,在html上,需要定义一个存放id的家伙。程序通过attr就是来取这个值,保证每一行都有自己唯一的值。

thislinetop,主要是用来和鼠标移动位置进行高度计算,然后根据行高、索引值来判断是移动到哪一行了。还有个作用是用来确定是否按在了移动锚点上,如果有值,说明是,那后面的mouseup就是成立的,如果没有值,说明没有按到锚点上,mouseup不执行任何操作。为什么要这样做呢?因为不管在页面的什么位置点鼠标,都会触发mouseup事件,如果没有一个判断,就会不停的执行,那就会产生一些问题。

topdistance和downdistance,用来判断鼠标有没有移出列表的范围,如果移除了,也就是鼠标移动的距离大于topdistance或downdistance,那就可以判断为需要移动到第一行或最后一行。

mousedown事件主要做的,就是这几件事情,当然,为了效果,还可以增加一些东西

 

. 代码如下:


$("#"+setting.linepre+dgid).css('background',setting.linehighlight); //高亮移动行
var left=e.pagex+20;
var top=e.pagey;
dg_tips(left,top);  //创建一个提示层
$('body').css('cursor','move'); //更改页面的鼠标手势
$("body").disableselection(); //禁止按下鼠标后移动鼠标时选中页面元素
setting.frame.mousemove(function(e){  //让提示层跟随鼠标移动
$("#dgf").css({"left":e.pagex+setting.tipsoffsetleft+'px',"top":e.pagey+'px'});
});

 

这些的目的,只是让操作起来更有效果,比如高亮行,就是要让用户知道,他们操作的是哪一行。提示层的作用也一样。

关于禁止选中,.disableselection();这是jquery_ui里带的方法,如果你有使用jquery_ui,那可以直接使用它,如果没有使用,可以这样做

 

. 代码如下:


$('body').each(function() {          
 $(this).attr('unselectable', 'on').css({
  '-moz-user-select':'none',
  '-webkit-user-select':'none',
  'user-select':'none'
 }).each(function() {
  this.onselectstart = function() { return false; };
 });
});

 

取消禁止选择

 

. 代码如下:


$('body').each(function() {          
 $(this).attr('unselectable', '').css({
  '-moz-user-select':'',
  '-webkit-user-select':'',
  'user-select':''
 });
});

 

考虑到通用性,所以后面的代码里,不会使用.disableselection();

好了,下面是mouseup事件。这里mouseup事件是绑定在body上的,因为mouseup如果只是绑定在锚点上,那当鼠标移出锚点的时候,再松开鼠标,会发现,这个mouseup事件不执行了,因为它会认为是别的对象的mouseup。所以,最保险的方法是用$('body').mouseup。这样基本上就不会有问题。

mouseup触发后,首先就要判断thislinetop是不是有值,防止无意义的事件执行。跟着判断鼠标移动的距离是正还是负,也就是向上移动还是向下移动。

var movedistance=e.pagey-thislinetop;

根据不同的方向作不同的处理

 

. 代码如下:


if(movedistance<0){
 if(thisindex!=0){
  movedistance=math.abs(movedistance);  //为负数的时候,取一下绝对值
  if(movedistance>lineheight/2){ //判断移动距离是否超过行高的1/2
   if(movedistance>topdistance){ //如果移动距离大于该行到顶边的距离
    focusindex=0;
   }else{
    focusindex=thisindex-math.ceil(movedistance/lineheight);
   }
   $("."+setting.dgline).eq(focusindex).before($("#"+setting.linepre+dgid));//将该行插入目标位置
  }
 }
}else{
 if(thisindex!=linenum-1){
  if(movedistance>lineheight/2+lineheight){
   if(movedistance>downdistance){
    focusindex=linenum-1;
   }else{
    focusindex=thisindex+math.ceil(movedistance/lineheight)-1;
   }
   $("."+setting.dgline).eq(focusindex).after($("#"+setting.linepre+dgid));
  }
 }
}

 

之所以判断移动距离是否超过行高的1/2,是因为如果只移动一小点,可以视作不移动。在计算目标索引值的时候,使用了math.ceil,最进位,而当移动距离大于0的时候,取了进位还要-1,因为是向下嘛。

向上移动和向下移动使用了不同的插入方法,before和after,可以试着想一下为什么要使用before和after。

移动完了,还得把按下鼠标时使用的效果给去除掉

 

. 代码如下:


$("#dgf").remove();//移除提示层
$("#"+setting.linepre+dgid).css('background','');//将高亮的行变为普通
dgid='';//将移动行的id值空
thislinetop=0;//将移动行的top值至0
$('body').css('cursor','default');//更改鼠标手势为默认

 

基本上的情况就是这样,主要问题就是在处理移动和判断在哪里插入的问题上。其它的都非常简单。

下面给出完整的封装程序,包括更新数据部分

 

. 代码如下:


/*
*
*  draglist.js
*  @author fuweiyi <fuwy@foxmail.com>

*/
(function($){
 $.fn.draglist=function(setting){
  var _setting = {
   frame : $(this),
   dgline : 'dll',
   dgbutton : 'dlb',
   id : 'action-id',
   linepre : 'list_',
   linehighlight : '#ffffcc',
   tipsopacity : 80,
   tipsoffsetleft : 20,
   tipsoffsettop : 0,
   jsonurl : '',
   jsondata : {},
   maskloaddingicon : '',
   maskbackgroundcolor : '#999',
   maskopacity : 30,
   maskcolor : '#000',
   maskloadicon:'',
  };
  var setting = $.extend(_setting,setting);

 

  var dgid='',thisindex,thislinetop=0,topdistance,downdistance;
  var tbodyheight=setting.frame.outerheight();
  var linenum=$("."+setting.dgline).length;
  var lineheight=math.ceil(tbodyheight/linenum);

  $("."+setting.dgbutton).mousedown(function(e){
   dgid=$(this).attr(setting.id);
   thisindex=$("#"+setting.linepre+dgid).index();
   var left=e.pagex+20;
   var top=e.pagey;
   thislinetop=$("#"+setting.linepre+dgid).offset().top;
   topdistance=thisindex*lineheight;
   downdistance=(linenum-thisindex-1)*lineheight;
   $("#"+setting.linepre+dgid).css('background',setting.linehighlight);

   dg_tips(left,top);
   $('body').css('cursor','move');
   unselect();
   setting.frame.mousemove(function(e){
    $("#dgf").css({"left":e.pagex+setting.tipsoffsetleft+'px',"top":e.pagey+'px'});
   });
  });

  $('body').mouseup(function(e){
   if(thislinetop>0){
    var movedistance=e.pagey-thislinetop;
    if(movedistance<0){
     if(thisindex!=0){
      movedistance=math.abs(movedistance);
      if(movedistance>lineheight/2){
       if(movedistance>topdistance){
        focusindex=0;
       }else{
        focusindex=thisindex-math.ceil(movedistance/lineheight);
       }
       $("."+setting.dgline).eq(focusindex).before($("#"+setting.linepre+dgid));
       dg_update(thisindex,focusindex);
      }
     }
    }else{
     if(thisindex!=linenum-1){
      if(movedistance>lineheight/2+lineheight){
       if(movedistance>downdistance){
        focusindex=linenum-1;
       }else{
        focusindex=thisindex+math.ceil(movedistance/lineheight)-1;
       }
       $("."+setting.dgline).eq(focusindex).after($("#"+setting.linepre+dgid));
       dg_update(thisindex,focusindex);
      }
     }
    }
    $("#dgf").remove();
    $("#"+setting.linepre+dgid).css('background','');
    dgid='';
    thislinetop=0;
    $('body').css('cursor','default');
    onselect();
   }
  });

  function dg_update(thisindex,focusindex){
   dg_mask();
   var start=thisindex<focusindex?thisindex:focusindex;
   var end=thisindex<focusindex?focusindex:thisindex;
   var ids='',vals='';
   for(var i=start;i<=end;i++){
    ids+=i==start?$("."+setting.dgline).eq(i).attr(setting.id):','+$("."+setting.dgline).eq(i).attr(setting.id);
    vals+=i==start?i:','+i;
   }
   $.getjson(setting.jsonurl,{'do':'changeorders','ids':ids,'vals':vals},function(d){
    $("#dg_mask").remove(); 
   });
  }

  function dg_mask(){
   var w=setting.frame.outerwidth();
   var h=setting.frame.outerheight();
   var top=setting.frame.offset().top;
   var left=setting.frame.offset().left;
   var mask="<p id='dg_mask'><img src='"+setting.maskloadicon+"' alt='' /> 正在使劲的保存...</p>";
   $('body').append(mask);
   $("#dg_mask").css({"background":"#999","position":'absolute','width':w+'px','height':h+'px','line-height':h+'px','top':top+'px','left':left+'px','filter':'alpha(opacity='+setting.maskopacity+')','moz-opacity':setting.maskopacity/100,'opacity':setting.maskopacity/100,'text-align':'center','color':'#000'});
  }

  function dg_tips(left,top){
   var floatp="<p id='dgf' style='padding:5px 10px;border:1px solid #444;background-color:#fff;filter:alpha(opacity="+setting.tipsopacity+");moz-opacity:"+setting.tipsopacity/100+";opacity:"+setting.tipsopacity/100+";position:absolute;left:"+left+"px;top:"+top+"px;'>移动一条记录</p>";
   $('body').append(floatp);
  }

  function unselect(){
   $('body').each(function() {          
    $(this).attr('unselectable', 'on').css({
     '-moz-user-select':'none',
     '-webkit-user-select':'none',
     'user-select':'none'
    }).each(function() {
     this.onselectstart = function() { return false; };
    });
   });
  }

  function onselect(){
   $('body').each(function() {          
    $(this).attr('unselectable', '').css({
     '-moz-user-select':'',
     '-webkit-user-select':'',
     'user-select':''
    });
   });
  }
 }
})(jquery);

 

使用

 

. 代码如下:


<table cellpadding="5" cellspacing="0" class="listtable" id="listtable">
 <thead>
  <tr>
   <td class="td50">拖动</td>
   <td class="td220">名称</td>
  </tr>
 </thead>
 <tbody id="drag_table">
  <!--{loop $lists $k $list}-->
  <tr id="list_{$list['id']}" action-id="{$list['id']}" class="drag_line">
   <td><p class="drag_orders" action-id="{$list['id']}"><img src="{sys_url}views/admin/images/move.png" alt="" /></p></p></td>
   <td>这里是一行</td>
  </tr>
  <!--{/loop}-->
 </tbody>
</table>
<script type="text/javascript">
$("#drag_table").draglist({
 dgline:'drag_line',
 dgbutton:'drag_orders',
 id:'action-id',
 linepre:'list_',
 jsonurl:'{_admin}?controller=contents&method=focus_form',
 maskloadicon:'{sys_url}views/admin/images/loading.gif'
});
</script>

 

参数主要是dgline,dgbutton,id,linepre和jsonurl,通过看html代码,应该不难理解。

关于拖动排序就是这么多了,当然还可以把效果做得更漂亮些,提示更清楚点,有助于用户体验。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网