可拖動的DIV續


之前寫過一篇可拖動的DIV講如何實現可拖動的元素,最后提出了幾點不足,這篇文章主要就是回答着三個問題

1. 瀏覽器兼容性

2. 邊界檢查

3. 拖動卡頓、失靈

先附上上次代碼

  1 <!DOCTYPE html>
  2 <html>
  3     <head>
  4         <title>Test</title>
  5         <style type="text/css" >
  6             html,body
  7             {
  8                 height:100%;
  9                 width:100%;
 10                 padding:0;
 11                 margin:0;
 12             }
 13             
 14             .dialog
 15             {
 16                 width:250px;
 17                 height:250px;
 18                 position:absolute;
 19                 background-color:#ccc;
 20                 -webkit-box-shadow:1px 1px 3px #292929;
 21                 -moz-box-shadow:1px 1px 3px #292929;
 22                 box-shadow:1px 1px 3px #292929;
 23                 margin:10px;
 24             }
 25             
 26             .dialog-title
 27             {
 28                 color:#fff;
 29                 background-color:#404040;
 30                 font-size:12pt;
 31                 font-weight:bold;
 32                 padding:4px 6px;
 33                 cursor:move;
 34             }
 35             
 36             .dialog-content
 37             {
 38                 padding:4px;
 39             }
 40         </style>
 41     </head>
 42     <body>
 43         <div id="dlgTest" class="dialog">
 44             <div class="dialog-title">Dialog</div>
 45             <div class="dialog-content">
 46                 This is a draggable test.
 47             </div>
 48         </div>
 49         <script type="text/javascript">
 50             var Dragging=function(validateHandler){ //參數為驗證點擊區域是否為可移動區域,如果是返回欲移動元素,負責返回null
 51                 var draggingObj=null; //dragging Dialog
 52                 var diffX=0;
 53                 var diffY=0;
 54                 
 55                 function mouseHandler(e){
 56                     switch(e.type){
 57                         case 'mousedown':
 58                             draggingObj=validateHandler(e);//驗證是否為可點擊移動區域
 59                             if(draggingObj!=null){
 60                                 diffX=e.clientX-draggingObj.offsetLeft;
 61                                 diffY=e.clientY-draggingObj.offsetTop;
 62                             }
 63                             break;
 64                         
 65                         case 'mousemove':
 66                             if(draggingObj){
 67                                 draggingObj.style.left=(e.clientX-diffX)+'px';
 68                                 draggingObj.style.top=(e.clientY-diffY)+'px';
 69                             }
 70                             break;
 71                         
 72                         case 'mouseup':
 73                             draggingObj =null;
 74                             diffX=0;
 75                             diffY=0;
 76                             break;
 77                     }
 78                 };
 79                 
 80                 return {
 81                     enable:function(){
 82                         document.addEventListener('mousedown',mouseHandler);
 83                         document.addEventListener('mousemove',mouseHandler);
 84                         document.addEventListener('mouseup',mouseHandler);
 85                     },
 86                     disable:function(){
 87                         document.removeEventListener('mousedown',mouseHandler);
 88                         document.removeEventListener('mousemove',mouseHandler);
 89                         document.removeEventListener('mouseup',mouseHandler);
 90                     }
 91                 }
 92             }
 93 
 94             function getDraggingDialog(e){
 95                 var target=e.target;
 96                 while(target && target.className.indexOf('dialog-title')==-1){
 97                     target=target.offsetParent;
 98                 }
 99                 if(target!=null){
100                     return target.offsetParent;
101                 }else{
102                     return null;
103                 }
104             }
105             
106             Dragging(getDraggingDialog).enable();
107         </script>
108     </body>
109 </html>
View Code

 

瀏覽器兼容性

這個是最好解決的問題了,看看上面代碼涉及到瀏覽器兼容性的地方無非就是event獲取及事件源獲取、事件綁定,為此特意寫兩個函數來做此事。

 1  function addEvent(element, type, key, handler) {//綁定事件處理程序
 2             if (element[type + key])
 3                 return false;
 4             if (typeof element.addEventListener != "undefined") {
 5                 element[type + key] = handler;
 6                 element.addEventListener(type, handler, false);
 7             }
 8             else {
 9                 element['e' + type + key] = handler;
10                 element[type + key] = function () {
11                     element['e' + type + key](window.event); //解決IE瀏覽器event及this問題
12                 };
13                 element.attachEvent('on' + type, element[type + key]);
14             }
15             return true;
16         }
17 
18         function removeEvent(element, type, key) {//移除事件
19             if (!element[type + key])
20                 return false;
21 
22             if (typeof element.removeEventListener != "undefined") {
23                 element.removeEventListener(type, element[type + key], false);
24             }
25             else {
26                 element.detachEvent("on" + type, element[type + key]);
27                 element['e' + type + key] = null;
28             }
29 
30             element[type + key] = null;
31             return true;
32         }

 

使用這兩個函數用於添加和移除事件,就可以解決瀏覽器兼容性問題,有興趣的同學可以研究一下,這是根據jQuery作者John Resig寫法的改版,參數key是綁定函數的自定義唯一標識,用於removeEvent時取消綁定,改版后代碼是這樣

 1   var Dragging = function (validateHandler) { //參數為驗證點擊區域是否為可移動區域,如果是返回欲移動元素,負責返回null 
 2             var draggingObj = null; //dragging Dialog 
 3             var diffX = 0; 
 4             var diffY = 0;
 5 
 6             function mouseHandler(e) { 
 7                 switch (e.type) { 
 8                     case 'mousedown': 
 9                         draggingObj = validateHandler(e);//驗證是否為可點擊移動區域 
10                         if (draggingObj != null) { 
11                             diffX = e.clientX - draggingObj.offsetLeft; 
12                             diffY = e.clientY - draggingObj.offsetTop; 
13                         } 
14                         break;
15 
16                     case 'mousemove': 
17                         if (draggingObj) { 
18                             draggingObj.style.left = (e.clientX - diffX) + 'px'; 
19                             draggingObj.style.top = (e.clientY - diffY) + 'px'; 
20                         } 
21                         break;
22 
23                     case 'mouseup': 
24                         draggingObj = null; 
25                         diffX = 0; 
26                         diffY = 0; 
27                         break; 
28                 } 
29             };
30 
31             return { 
32                 enable: function () { 
33                     addEvent(document, 'mousedown', 'drag-down', mouseHandler); 
34                     addEvent(document, 'mousemove', 'drag-move', mouseHandler); 
35                     addEvent(document, 'mouseup', 'drag-up', mouseHandler); 
36                 }, 
37                 disable: function () { 
38                     removeEvent(document, 'mousedown', 'drag-down'); 
39                     removeEvent(document, 'mousemove', 'drag-move'); 
40                     removeEvent(document, 'mouseup', 'drag-up'); 
41                 } 
42             } 
43         }
44 
45         function getDraggingDialog(e) { 
46             var target = e && e.target ? e.target : window.event.srcElement; 
47             while (target && target.className.indexOf('dialog-title') == -1) { 
48                 target = target.offsetParent; 
49             } 
50             if (target != null) { 
51                 return target.offsetParent; 
52             } else { 
53                 return null; 
54             } 
55         }
56 
57         Dragging(getDraggingDialog).enable(); 

 

邊界處理

這個問題說起來也簡單,可以在函數調用的時候傳入邊界值,每次移動的時候判斷是否出了邊界,這樣改動一下

 

 1         var Dragging = function (conf) { //參數為驗證點擊區域是否為可移動區域,如果是返回欲移動元素,負責返回null
 2             var draggingObj = null; //dragging Dialog
 3             var diffX = 0, diffY = 0;
 4 
 5             var minX = conf.left != undefined ? conf.left : Number.NEGATIVE_INFINITY;
 6             var maxX = conf.right != undefined ? conf.right : Number.POSITIVE_INFINITY;
 7             var minY = conf.top != undefined ? conf.top : Number.NEGATIVE_INFINITY;
 8             var maxY = conf.bottom != undefined ? conf.bottom : Number.POSITIVE_INFINITY;
 9 
10             var draggingObjWidth = 0,
11                 draggingObjHeight = 0;
12 
13             function mouseHandler(e) {
14                 switch (e.type) {
15                     case 'mousedown':
16                         draggingObj = conf.validateHandler(e);//驗證是否為可點擊移動區域
17                         if (draggingObj != null) {
18                             diffX = e.clientX - draggingObj.offsetLeft;
19                             diffY = e.clientY - draggingObj.offsetTop;
20                             var size = draggingObj.getBoundingClientRect();
21                             draggingObjWidth = size.right - size.left;
22                             draggingObjHeight = size.bottom - size.top;
23                         }
24                         break;
25 
26                     case 'mousemove':
27                         if (draggingObj) {
28                             var x = e.clientX - diffX;
29                             var y = e.clientY - diffY;
30                             if (x > minX && x < maxX - draggingObjWidth) {
31                                 draggingObj.style.left = x + 'px';
32                             }
33                             if (y > minY && y < maxY - draggingObjHeight) {
34                                 draggingObj.style.top = y + 'px';
35                             }
36                         }
37                         break;
38 
39                     case 'mouseup':
40                         draggingObj = null;
41                         diffX = 0;
42                         diffY = 0;
43                         break;
44                 }
45             };
46 
47             return {
48                 enable: function () {
49                     addEvent(document, 'mousedown', 'drag-down', mouseHandler);
50                     addEvent(document, 'mousemove', 'drag-move', mouseHandler);
51                     addEvent(document, 'mouseup', 'drag-up', mouseHandler);
52                 },
53                 disable: function () {
54                     removeEvent(document, 'mousedown', 'drag-down');
55                     removeEvent(document, 'mousemove', 'drag-move');
56                     removeEvent(document, 'mouseup', 'drag-up');
57                 }
58             }
59         }
60 
61         function getDraggingDialog(e) {
62             var target = e && e.target ? e.target : window.event.srcElement;
63             while (target && target.className.indexOf('dialog-title') == -1) {
64                 target = target.offsetParent;
65             }
66             if (target != null) {
67                 return target.offsetParent;
68             } else {
69                 return null;
70             }
71         }
72 
73         var config = {
74             validateHandler: getDraggingDialog,
75             top: document.documentElement.clientTop,
76             right: document.documentElement.clientWidth,
77             bottom: document.documentElement.clientHeight,
78             left: document.documentElement.clientLeft
79         }
80 
81         Dragging(config).enable();

 

如果希望Dialog只能在可視窗口拖動,就可以像上面那樣對config參數自定義四個邊界值,如果仍然希望沒有邊界的拖動,則可以四個邊界問題不處理,但是validateHandler屬性是必須的。

拖動卡頓、失效

關於拖動卡頓在復雜的頁面有位明顯,一個重要原因就是拖動的時候計算太多導致,不要以為在若動的時候頁面就僅僅處理拖動部分的代碼,沒拖動細微的一下頁面都要進行reflow,計算布局所有頁面元素的位置,所以復雜的頁面自然會卡頓,我們能夠處理的只能是是代碼的計算盡量簡單,為了防止誤導讀者,我在上面的版本中其實已經做了此項工作,把能夠提前計算的的變量值盡量都在函數初始化、mousedown的時候做,再就是盡量使用值變量,避免JavaScript[頻繁層層搜索變量引用,看一下低效版的拖動(可不要學會)

 

 1    function mouseHandler(e) {
 2             switch (e.type) {
 3                 case 'mousedown':
 4                     draggingObj = conf.validateHandler(e);//驗證是否為可點擊移動區域
 5                     break;
 6 
 7                 case 'mousemove':
 8                     if (draggingObj) {
 9                         diffX = e.clientX - draggingObj.offsetLeft; //如果這兩句也不定義變量,每次使用都要取event的屬性值和draggingObj的屬性值
10                         diffY = e.clientY - draggingObj.offsetTop;
11                         var size = draggingObj.getBoundingClientRect(); //每移動一下都要算一下大小,實際沒必要,拖動不不會改變元素大小
12 
13                         if ((e.clientX - diffX) > minX && (e.clientX - diffX) < maxX - (size.right - size.left)) {//每次都要再算兩遍e.clientX - diffX
14                             draggingObj.style.left = x + 'px';
15                         }
16                         if ((e.clientY - diffY) > minY && (e.clientY - diffY) < maxY - (size.bottom - size.top)) {//每次都要再算兩遍e.clientY - diffY
17                             draggingObj.style.top = y + 'px';
18                         }
19                     }
20                     break;
21 
22                 case 'mouseup':
23                     draggingObj = null;
24                     diffX = 0;
25                     diffY = 0;
26                     minX = 0;
27                     break;
28             }
29         };

 

有同學會說了你都處理了為什么每次還是會拖着拖着就鼠標就出去了,然后就失效了呢?仔細看看每次失效的時候頁面上中會伴隨着文字被選中,而且仔細觀察這個真的會影響拖動,處理一下,拖動的時候不允許選中文字

.disable-select *
        {
            -moz-user-select: none;
            -ms-user-select: none;
            -webkit-user-select: none;
            user-select: none;
        }

 

 1 function mouseHandler(e) {
 2             switch (e.type) {
 3                 case 'mousedown':
 4                     draggingObj = conf.validateHandler(e);//驗證是否為可點擊移動區域
 5                     if (draggingObj != null) {
 6                         diffX = e.clientX - draggingObj.offsetLeft;
 7                         diffY = e.clientY - draggingObj.offsetTop;
 8 
 9                         var size = draggingObj.getBoundingClientRect();
10                         draggingObjWidth = size.right - size.left;
11                         draggingObjHeight = size.bottom - size.top;
12                         document.body.className += '  disable-select'; //禁止選中
13                         document.body.onselectstart = function () { return false; };
14                     }
15                     break;
16 
17                 case 'mousemove':
18                     if (draggingObj) {
19                         var x = e.clientX - diffX;
20                         var y = e.clientY - diffY;
21                         if (x > minX && x < maxX - draggingObjWidth) {
22                             draggingObj.style.left = x + 'px';
23                         }
24                         if (y > minY && y < maxY - draggingObjHeight) {
25                             draggingObj.style.top = y + 'px';
26                         }
27                     }
28                     break;
29 
30                 case 'mouseup':
31                     draggingObj = null;
32                     diffX = 0;
33                     diffY = 0;
34                     document.body.className = document.body.className.replace('  disable-select', '');
35                     document.body.onselectstart = null;
36                     break;
37             }
38         };

最后

最后的源碼就是這樣的

 

  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4     <title>Test</title>
  5     <style type="text/css">
  6         html, body
  7         {
  8             height: 100%;
  9             width: 100%;
 10             padding: 0;
 11             margin: 0;
 12         }
 13 
 14         .dialog
 15         {
 16             width: 250px;
 17             height: 250px;
 18             position: absolute;
 19             background-color: #ccc;
 20             -webkit-box-shadow: 1px 1px 3px #292929;
 21             -moz-box-shadow: 1px 1px 3px #292929;
 22             box-shadow: 1px 1px 3px #292929;
 23         }
 24 
 25         .dialog-title
 26         {
 27             color: #fff;
 28             background-color: #404040;
 29             font-size: 12pt;
 30             font-weight: bold;
 31             padding: 4px 6px;
 32             cursor: move;
 33         }
 34 
 35         .dialog-content
 36         {
 37             padding: 4px;
 38         }
 39 
 40         .disable-select *
 41         {
 42             -moz-user-select: none;
 43             -ms-user-select: none;
 44             -webkit-user-select: none;
 45             user-select: none;
 46         }
 47     </style>
 48 </head>
 49 <body>
 50     <div id="dlgTest" class="dialog">
 51         <div class="dialog-title">Dialog</div>
 52         <div class="dialog-content">
 53             This is a draggable test.
 54         </div>
 55     </div>
 56 
 57     <script type="text/javascript">
 58         function addEvent(element, type, key, handler) {//綁定事件處理程序
 59             if (element[type + key])
 60                 return false;
 61             if (typeof element.addEventListener != "undefined") {
 62                 element[type + key] = handler;
 63                 element.addEventListener(type, handler, false);
 64             }
 65             else {
 66                 element['e' + type + key] = handler;
 67                 element[type + key] = function () {
 68                     element['e' + type + key](window.event); //解決IE瀏覽器event及this問題
 69                 };
 70                 element.attachEvent('on' + type, element[type + key]);
 71             }
 72             return true;
 73         }
 74 
 75         function removeEvent(element, type, key) {//移除事件
 76             if (!element[type + key])
 77                 return false;
 78 
 79             if (typeof element.removeEventListener != "undefined") {
 80                 element.removeEventListener(type, element[type + key], false);
 81             }
 82             else {
 83                 element.detachEvent("on" + type, element[type + key]);
 84                 element['e' + type + key] = null;
 85             }
 86 
 87             element[type + key] = null;
 88             return true;
 89         }
 90     </script>
 91 
 92     <script type="text/javascript">
 93         var Dragging = function (conf) { //參數為驗證點擊區域是否為可移動區域,如果是返回欲移動元素,負責返回null
 94             var draggingObj = null; //dragging Dialog
 95             var diffX = 0, diffY = 0;
 96 
 97             var minX = conf.left != undefined ? conf.left : Number.NEGATIVE_INFINITY;
 98             var maxX = conf.right != undefined ? conf.right : Number.POSITIVE_INFINITY;
 99             var minY = conf.top != undefined ? conf.top : Number.NEGATIVE_INFINITY;
100             var maxY = conf.bottom != undefined ? conf.bottom : Number.POSITIVE_INFINITY;
101 
102             var draggingObjWidth = 0,
103                 draggingObjHeight = 0;
104 
105             function mouseHandler(e) {
106                 switch (e.type) {
107                     case 'mousedown':
108                         draggingObj = conf.validateHandler(e);//驗證是否為可點擊移動區域
109                         if (draggingObj != null) {
110                             diffX = e.clientX - draggingObj.offsetLeft;
111                             diffY = e.clientY - draggingObj.offsetTop;
112 
113                             var size = draggingObj.getBoundingClientRect();
114                             draggingObjWidth = size.right - size.left;
115                             draggingObjHeight = size.bottom - size.top;
116                             document.body.className += '  disable-select'; //禁止選中
117                             document.body.onselectstart = function () { return false; };
118                         }
119                         break;
120 
121                     case 'mousemove':
122                         if (draggingObj) {
123                             var x = e.clientX - diffX;
124                             var y = e.clientY - diffY;
125                             if (x > minX && x < maxX - draggingObjWidth) {
126                                 draggingObj.style.left = x + 'px';
127                             }
128                             if (y > minY && y < maxY - draggingObjHeight) {
129                                 draggingObj.style.top = y + 'px';
130                             }
131                         }
132                         break;
133 
134                     case 'mouseup':
135                         draggingObj = null;
136                         diffX = 0;
137                         diffY = 0;
138                         document.body.className = document.body.className.replace('  disable-select','');
139                         document.body.onselectstart = null;
140                         break;
141                 }
142             };
143 
144             return {
145                 enable: function () {
146                     addEvent(document, 'mousedown', 'drag-down', mouseHandler);
147                     addEvent(document, 'mousemove', 'drag-move', mouseHandler);
148                     addEvent(document, 'mouseup', 'drag-up', mouseHandler);
149                 },
150                 disable: function () {
151                     removeEvent(document, 'mousedown', 'drag-down');
152                     removeEvent(document, 'mousemove', 'drag-move');
153                     removeEvent(document, 'mouseup', 'drag-up');
154                 }
155             }
156         }
157 
158         function getDraggingDialog(e) {
159             var target = e && e.target ? e.target : window.event.srcElement;
160             while (target && target.className.indexOf('dialog-title') == -1) {
161                 target = target.offsetParent;
162             }
163             if (target != null) {
164                 return target.offsetParent;
165             } else {
166                 return null;
167             }
168         }
169 
170         var config = {
171             validateHandler: getDraggingDialog,
172             top: document.documentElement.clientTop,
173             right: document.documentElement.clientWidth,
174             bottom: document.documentElement.clientHeight,
175             left: document.documentElement.clientLeft
176         };
177 
178         Dragging(config).enable();
179     </script>
180 </body>
181 </html>
View Code

 

試試真的好了很多,然而鼠標要是移動的快還是會拖離,以為就是這樣了呢,但試了試jQuery的Dialog控件,拖動基本流暢,這讓人情何以堪,今天天氣好,出去找妹子了,改天研究研究jQuery是怎么寫的吧


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM