基于JQuery的列表拖動(dòng)排序?qū)崿F(xiàn)代碼
要求
拖動(dòng)排序,從名字就不難想像,就是按住一行數(shù)據(jù)拖到想要的排序位置,保存新的排序隊(duì)列。
思路
首先給列表行建立錨點(diǎn),綁定mousedown和mouseup事件,當(dāng)鼠標(biāo)移動(dòng)到想要插入的位置時(shí),將對象行移動(dòng)到目標(biāo)行,然后對其經(jīng)過的所有行進(jìn)行排序處理。
思路很簡單,但這里面仍然有幾個(gè)問題要注意
1、移動(dòng)到什么位置可以視作要插入到目標(biāo)行的位置。
2、移動(dòng)出了頂端和底端時(shí),判斷為第一和最后。
3、向上移動(dòng)和向下移動(dòng)的處理
解決
關(guān)于事件
Javascript里鼠標(biāo)按下和放開事件為onmousedown,onmouseup,JQuery里是mousedown,mouseup,所以,這里使用mousedown和mouseup
首先,要知道界面有多少行,每一行有多高,因?yàn)橐袛嗍髽?biāo)的移動(dòng)距離
var tbodyHeight=setting.frame.outerHeight(); //setting.frame,父對象
var lineNum=$("."+setting.dgLine).length; //setting.dgLine,每一行的classname
var lineHeight=Math.ceil(tbodyHeight/lineNum);
之所有要取lineNum(行數(shù)),除了計(jì)算行高外,還有個(gè)目的是要使用index(),通過序列索引值來進(jìn)行移動(dòng)行到目標(biāo)位置
當(dāng)mousedown事件觸發(fā)后,就要開始計(jì)算鼠標(biāo)移動(dòng)的距離,用于判斷該行到底要移動(dòng)到什么位置
dgid=$(this).attr(setting.id); //移動(dòng)行的ID,setting.id,是每一行用來標(biāo)記ID的名稱
thisIndex=$("#"+setting.linePre+dgid).index(); //該行的索引,setting.linePre,每一行ID關(guān)輟
thisLineTop=$("#"+setting.linePre+dgid).offset().top; //該行的top值
topDistance=thisIndex*lineHeight; //該行距離第一行頂端的距離
downDistance=(lineNum-thisIndex-1)*lineHeight; //該行距離最后一行底端的距離
dgid主要是用來區(qū)分每一行的標(biāo)識,一般的列表都是程序循環(huán)輸出來的,如果沒有這樣一個(gè)ID,就分不出哪行是哪行,所以,在HTML上,需要定義一個(gè)存放ID的家伙。程序通過attr就是來取這個(gè)值,保證每一行都有自己唯一的值。
thisLineTop,主要是用來和鼠標(biāo)移動(dòng)位置進(jìn)行高度計(jì)算,然后根據(jù)行高、索引值來判斷是移動(dòng)到哪一行了。還有個(gè)作用是用來確定是否按在了移動(dòng)錨點(diǎn)上,如果有值,說明是,那后面的mouseup就是成立的,如果沒有值,說明沒有按到錨點(diǎn)上,mouseup不執(zhí)行任何操作。為什么要這樣做呢?因?yàn)椴还茉陧撁娴氖裁次恢命c(diǎn)鼠標(biāo),都會觸發(fā)mouseup事件,如果沒有一個(gè)判斷,就會不停的執(zhí)行,那就會產(chǎn)生一些問題。
topDistance和downDistance,用來判斷鼠標(biāo)有沒有移出列表的范圍,如果移除了,也就是鼠標(biāo)移動(dòng)的距離大于topDistance或downDistance,那就可以判斷為需要移動(dòng)到第一行或最后一行。
mousedown事件主要做的,就是這幾件事情,當(dāng)然,為了效果,還可以增加一些東西
$("#"+setting.linePre+dgid).css('background',setting.lineHighlight); //高亮移動(dòng)行
var left=e.pageX+20;
var top=e.pageY;
dg_tips(left,top); //創(chuàng)建一個(gè)提示層
$('body').css('cursor','move'); //更改頁面的鼠標(biāo)手勢
$("body").disableSelection(); //禁止按下鼠標(biāo)后移動(dòng)鼠標(biāo)時(shí)選中頁面元素
setting.frame.mousemove(function(e){ //讓提示層跟隨鼠標(biāo)移動(dòng)
$("#dgf").css({"left":e.pageX+setting.tipsOffsetLeft+'px',"top":e.pageY+'px'});
});
這些的目的,只是讓操作起來更有效果,比如高亮行,就是要讓用戶知道,他們操作的是哪一行。提示層的作用也一樣。
關(guān)于禁止選中,.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上的,因?yàn)閙ouseup如果只是綁定在錨點(diǎn)上,那當(dāng)鼠標(biāo)移出錨點(diǎn)的時(shí)候,再松開鼠標(biāo),會發(fā)現(xiàn),這個(gè)mouseup事件不執(zhí)行了,因?yàn)樗鼤J(rèn)為是別的對象的mouseup。所以,最保險(xiǎn)的方法是用$('body').mouseup。這樣基本上就不會有問題。
mouseup觸發(fā)后,首先就要判斷thisLineTop是不是有值,防止無意義的事件執(zhí)行。跟著判斷鼠標(biāo)移動(dòng)的距離是正還是負(fù),也就是向上移動(dòng)還是向下移動(dòng)。
var moveDistance=e.pageY-thisLineTop;
根據(jù)不同的方向作不同的處理
if(moveDistance<0){
if(thisIndex!=0){
moveDistance=Math.abs(moveDistance); //為負(fù)數(shù)的時(shí)候,取一下絕對值
if(moveDistance>lineHeight/2){ //判斷移動(dòng)距離是否超過行高的1/2
if(moveDistance>topDistance){ //如果移動(dòng)距離大于該行到頂邊的距離
focusIndex=0;
}else{
focusIndex=thisIndex-Math.ceil(moveDistance/lineHeight);
}
$("."+setting.dgLine).eq(focusIndex).before($("#"+setting.linePre+dgid));//將該行插入目標(biāo)位置
}
}
}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));
}
}
}
之所以判斷移動(dòng)距離是否超過行高的1/2,是因?yàn)槿绻灰苿?dòng)一小點(diǎn),可以視作不移動(dòng)。在計(jì)算目標(biāo)索引值的時(shí)候,使用了Math.ceil,最進(jìn)位,而當(dāng)移動(dòng)距離大于0的時(shí)候,取了進(jìn)位還要-1,因?yàn)槭窍蛳侣铩?/P>
向上移動(dòng)和向下移動(dòng)使用了不同的插入方法,before和after,可以試著想一下為什么要使用before和after。
移動(dòng)完了,還得把按下鼠標(biāo)時(shí)使用的效果給去除掉
$("#dgf").remove();//移除提示層
$("#"+setting.linePre+dgid).css('background','');//將高亮的行變?yōu)槠胀?BR>dgid='';//將移動(dòng)行的ID值空
thisLineTop=0;//將移動(dòng)行的Top值至0
$('body').css('cursor','default');//更改鼠標(biāo)手勢為默認(rèn)
基本上的情況就是這樣,主要問題就是在處理移動(dòng)和判斷在哪里插入的問題上。其它的都非常簡單。
下面給出完整的封裝程序,包括更新數(shù)據(jù)部分
/*
*
* 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;'>移動(dòng)一條記錄</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">拖動(dòng)</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>
參數(shù)主要是dgLine,dgButton,id,linePre和JSONUrl,通過看HTML代碼,應(yīng)該不難理解。
關(guān)于拖動(dòng)排序就是這么多了,當(dāng)然還可以把效果做得更漂亮些,提示更清楚點(diǎn),有助于用戶體驗(yàn)
- jquery實(shí)現(xiàn)表格行拖動(dòng)排序
- jQuery-ui插件sortable實(shí)現(xiàn)自由拖動(dòng)排序
- 針對后臺列表table拖拽比較實(shí)用的jquery拖動(dòng)排序
- jQuery拖動(dòng)元素并對元素進(jìn)行重新排序
- jQuery仿360導(dǎo)航頁圖標(biāo)拖動(dòng)排序效果代碼分享
- jquery實(shí)現(xiàn)的鼠標(biāo)拖動(dòng)排序Li或Table
- jquery對元素拖動(dòng)排序示例
- jQuery實(shí)現(xiàn)移動(dòng)端懸浮拖動(dòng)效果
- jQuery實(shí)現(xiàn)的簡單對話框拖動(dòng)功能示例
- jquery+css實(shí)現(xiàn)移動(dòng)端元素拖動(dòng)排序
相關(guān)文章
jquery lazyload延遲加載技術(shù)的實(shí)現(xiàn)原理分析
懶加載技術(shù)(簡稱lazyload)并不是新技術(shù),它是js程序員對網(wǎng)頁性能優(yōu)化的一種方案。lazyload的核心是按需加載。在大型網(wǎng)站中都有l(wèi)azyload的身影,例如谷歌的圖片搜索頁,迅雷首頁,淘寶網(wǎng),QQ空間等。2011-01-01jquery實(shí)現(xiàn)界面點(diǎn)擊按鈕彈出懸浮框
這篇文章主要為大家詳細(xì)介紹了jquery實(shí)現(xiàn)界面點(diǎn)擊按鈕彈出懸浮框,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03jQuery插件HighCharts繪制2D餅圖效果示例【附demo源碼下載】
這篇文章主要介紹了jQuery插件HighCharts繪制2D餅圖效果,結(jié)合完整實(shí)例形式分析了jQuery使用HighCharts插件繪制餅圖效果的操作步驟與相關(guān)實(shí)現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2017-03-03jQuery彈出層插件popShow(改進(jìn)版)用法示例
這篇文章主要介紹了jQuery彈出層插件popShow(改進(jìn)版)用法,對比前文分析了popShow插件的使用方法,需要的朋友可以參考下2017-01-01jQuery實(shí)現(xiàn)防止提交按鈕被雙擊的方法
這篇文章主要介紹了jQuery實(shí)現(xiàn)防止提交按鈕被雙擊的方法,涉及jQuery針對鼠標(biāo)按鍵的操作技巧以及preventDefault方法對元素默認(rèn)行為的修改技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-03-03JQuery和html+css實(shí)現(xiàn)帶小圓點(diǎn)和左右按鈕的輪播圖實(shí)例
下面小編就為大家?guī)硪黄狫Query和html+css實(shí)現(xiàn)帶小圓點(diǎn)和左右按鈕的輪播圖實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-07-07使用jQuery和PHP實(shí)現(xiàn)類似360功能開關(guān)效果
本文介紹了使用jQuery、PHP和MySQL實(shí)現(xiàn)類似360安全衛(wèi)士防火墻開啟關(guān)閉的開關(guān),可以將此功能應(yīng)用在產(chǎn)品功能的開啟和關(guān)閉功能上,需要的朋友可以參考下2014-02-02