基于JQuery的列表拖动排序实现代码
要求
拖动排序,从名字就不难想像,就是按住一行数据拖到想要的排序位置,保存新的排序队列。
思路
首先给列表行建立锚点,绑定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="<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代码,应该不难理解。
关于拖动排序就是这么多了,当然还可以把效果做得更漂亮些,提示更清楚点,有助于用户体验
相关文章
- 这篇文章主要介绍了jquery实现加载更多"转圈圈"效果,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-11-10
- 最近接了一个项目,其中有需求要用到jquery分页控件,上网也找到了需要分页控件,各种写法各种用法,都是很复杂,最终决定自己动手写一个jquery分页控件,全当是练练手了。写的不好,还请见谅,本分页控件在chrome测试过,其他的兼容性...2015-10-30
- 有时我们在页面上需要选择数值范围,如购物时选取价格区间,购买主机时自主选取CPU,内存大小配置等,使用直观的滑块条直接选取想要的数值大小即可,无需手动输入数值,操作简单又方便。HTML首先载入jQuery库文件以及jRange相关...2015-03-15
- 本文实例讲述了jQuery实现非常实用漂亮的select下拉菜单选择效果。分享给大家供大家参考,具体如下:先来看如下运行效果截图:在线演示地址如下:http://demo.jb51.net/js/2015/js-select-chose-style-menu-codes/具体代码如...2015-11-08
- 本文实例讲述了jquery实现的伪分页效果代码。分享给大家供大家参考,具体如下:这里介绍的jquery伪分页效果,在火狐下表现完美,IE全系列下有些问题,引入了jQuery1.7.2插件,代码里有丰富的注释,相信对学习jQuery有不小的帮助,期...2015-10-30
- JQuery使我们在开发Ajax应用程序的时候提高了效率,减少了许多兼容性问题,我们在Ajax项目中,遇到ajax异步获取数据出错怎么办,我们可以通过捕捉error事件来获取出错的信息。在没给大家介绍正文之前先给分享Jquery中AJAX参...2015-11-24
- 拜读一个开源框架,最想学到的就是设计的思想和实现的技巧。废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过,不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍我也不会照本宣科的翻译...2014-05-31
- 当页面打开时我们需要执行一些操作,这个时候如果我们选择使用jquery的话,需要重写他的3中方法,自我感觉没什么区 别,看个人喜好了,第二种感觉比较简单明了: 第一种: 复制代码 代码如下: <script type="text/javas...2014-06-07
jquery中常用的SET和GET$(”#msg”).html循环介绍
复制代码 代码如下: $(”#msg”).html(); //返回id为msg的元素节点的html内容。 $(”#msg”).html(”new content“); //将“new content” 作为html串写入id为msg的元素节点内容中,页面显示粗体的new content $(”...2013-10-13- jquery中jquery.offset().top / left用于获取div距离窗口的距离,jquery.position().top / left 用于获取距离父级div的距离(必须是绝对定位的div)。 (1)先介绍jquery.offset().top / left css: 复制代码 代码如下: *{ mar...2013-10-13
- 这篇文章主要为大家详细介绍了jQuery实现广告显示和隐藏动画,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-07-05
jQuery Mobile开发中日期插件Mobiscroll使用说明
这篇文章主要介绍了jQuery Mobile开发中日期插件Mobiscroll使用说明,需要的朋友可以参考下...2016-03-03- 直接为大家介绍制作过程,希望大家可以喜欢。HTML结构该页面切换特效的HTML结构使用一个<main>元素来作为页面的包裹元素,div.cd-cover-layer用于制作页面切换时的遮罩层,div.cd-loading-bar是进行ajax加载时的loading进...2015-10-30
jQuery 1.9使用$.support替代$.browser的使用方法
jQuery 从 1.9 版开始,移除了 $.browser 和 $.browser.version , 取而代之的是 $.support 。 在更新的 2.0 版本中,将不再支持 IE 6/7/8。 以后,如果用户需要支持 IE 6/7/8,只能使用 jQuery 1.9。 如果要全面支持 IE,并混合...2014-05-31- 本文实例讲述了jQuery实现鼠标滑过链接控制图片的滑动展开与隐藏效果。分享给大家供大家参考,具体如下:这里演示jQuery实现鼠标移动到链接上,滑动展开/隐藏图片效果,鼠标放在“上一页”“下一页”上,立即浮现出所对应的图...2015-10-30
jQuery+PHP发布的内容进行无刷新分页(Fckeditor)
这篇文章将使用jQuery,并结合PHP,将Fckeditor发布的内容进行分页,并且实现无刷新切换页面。 本文假设你是WEB开发人员,掌握了jQuery和PHP相关知识,并且熟知Fckeditor的配置和使用。...2015-10-23- jQuery实现带玻璃流光质感的手风琴特效是一款基于jQuery+CSS3实现的带玻璃流光质感的手风琴特效,分享给大家,具体如下效果图:具体代码如下:html代码: <section class="strips"> <article class="strips__strip"> <di...2015-11-24
- 有时候我们为了省事就操作键盘组合键去代替使用鼠标,我们今天就使用JQuery实现Ctrl+Enter提交表单。我们发帖时,在内容输入框中输入完内容后,可以点击“提交”按钮来发表内容。可是,如果你够“懒”,你可以不用动鼠标,只需按...2015-10-23
jQuery+slidereveal实现的面板滑动侧边展出效果
我们借助一款jQuery插件:slidereveal.js,可以使用它控制面板左右侧滑出与隐藏等效果,项目地址:https://github.com/nnattawat/slideReveal。如何使用首先在页面中加载jquery库文件和slidereveal.js插件。复制代码 代码如...2015-03-15- jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证变得更简单,同时提供了大量的定制选项,满足应用程序各种需求。该插件捆绑了一套有用的验证方法,包括 URL 和电子邮件验证,同时提供了一个用来编写用户自...2015-10-30