可拖动的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是怎么写的吧

posted @ 2013-08-31 20:23  谦行  阅读(3504)  评论(9编辑  收藏  举报