• 基于JQuery的列表拖动排序


    基于JQuery的拖动插件有几个都相当好用,效果也很好,但再好,还是自己琢磨一个最好。所以,我的理念就是即使实际项目中使用别人的程序,自己也得根据理解和想法写一个出来。仅当学习使用

    测试

    ID 拖动 名称
    1
    这里是第一行
    2
    这里是第二行
    3
    这里是第三行
    4
    这里是第四行
    5
    这里是第五行

    要求

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

    思路

    首先给列表行建立锚点,绑定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');//更改鼠标手势为默认

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

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

    View Code
    /*
    * 
    *  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="<div id='dg_mask'><img src='"+setting.maskLoadIcon+"' alt='' /> 正在使劲的保存...</div>";
                $('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 floatdiv="<div 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;'>移动一条记录</div>";
                $('body').append(floatdiv);
            }
            
            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><div class="drag_orders" action-id="{$list['id']}"><img src="{SYS_URL}views/admin/images/move.png" alt="" /></div></div></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代码,应该不难理解。

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

  • 相关阅读:
    DMR 系统平方根升余弦滚降滤波器设计SRRC及仿真图
    相似项
    第三种主题暗黑系
    汽油性能
    放平心态
    Python 中的哈希表
    第二种主题
    win7旗舰版 一键激活
    java与c++的不同感受
    c代码待用c++代码
  • 原文地址:https://www.cnblogs.com/onlyfu/p/3013728.html
Copyright © 2020-2023  润新知