继上一篇博客个性化定制功能后,今天我又花了足足一天时间,实现了使用js拖拽定制模块功能。主要包括三步:1.拖拽模块;2.计算坐标位置;3.完成拖拽,保存当前布局位置。
一、拖拽功能
如果只单实现简单的拖拽功能,那其实很简单,就只要注意拖拽时选中文本一些问题。由于我这里还拖拽有一些区域限制等,模块又都是从后台动态输出的,就做成一个函数类,可能实现出来就没那么通用了。拖拽的过程一般我们会用到几个事件:鼠标开始按下onmousedown事件,拖动事件onmousemove,松开鼠标完成拖拽事件onmouseup。对于几个DOM元素都有相同的事件,一般不需要每个DOM元素逐一添加事件监听,可以添加一个事件到它们公共的父元素上,这样感觉更好,然后通过事件的target或srcElement捕获触发事件源元素,这里的拖拽也是这样来实现的,onmousedown事件是监听拖拽模块的父容器元素,这个方法有点不好的就是可能获得事件发生源并不是你想要的,可能获得的是它的子元素,所以这里可能还需要额外加一个是否为parentNode的判断。onmousedown事件第一步先要判断按的元素是否为可拖拽的句柄,然后再给document添加onmousemove、onmouseup事件。onmousemove里是主要的逻辑处理的事件,先根据event事件获取鼠标位置,然后设置拖拽模块的top和left值,最后使用window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty()来取消拖拽过程中选择文本。
二、计算坐标位置
在拖拽模块过程还需要判断元素的坐标位置,因为拖动的模块只能放在指定的几个区域内,所以就需要动态判断是否在指定区域内容,在区域内的那个模块前,这些都需要根据元素的坐标来判断。先判断是否区域,区域栏为树直排列的容器,因此,只需要判断当前鼠标x坐标>=区域x坐标,并且鼠标x坐标<=区域x坐标+区域宽度即可。同理,确定区域后,判断模块是根据当前鼠标y坐标>=模块y坐标,并且鼠标y坐标<=模块y坐标+模块高度,相当于判断鼠标是否在某个元素上,依此可以定位当前拖放元素的区域模块位置,并insertBefore插入一个空元素顶位,再在onmouseup事件里把顶位的空元素替换为当前拖拽的元素,清空拖动时设置的position等样式。在拖放过程中,冒似在IE里计算还有点问题,有时候发现计算出来的位置不正确。另外还发现一个bug,在拖拽时如果用滑轮滚动页面,拖动的元素会滑落在鼠标位置,导致隔空拖动,因为拉滚动条的时候并不会触发onmousemove事件,去看了下iGoogle的首页,发现也存在这个问题,我想可能需要添加window.onscoll事件监视才能解决了。
三、保存拖拽后位置
拖拽完成onmouseup事件处理相对简单,因为在onmousemove事件中已确认拖动模块的位置了即上面所说的顶位的空元素,此时只需要replaceChild为拖拽的模块,但还有一个重要的步骤,那就是前台与后台交互,把当前客户端拖拽后的模块位置状态保存起来,或是写入进数据库。这可能就是一个比较复杂的过程了,因为这干系到数据库表存放结构等,我这里的模块表存放字段有模块ID、区域ID、排序号等,所以客户端保存的时候至少要能获取这些信息,这里我在与后台动态输出模块时就有一个约定,那就是每个模块和区域的DIV里的id或class都包含这些信息,以便客户端能用js方便取到,至于排序则是遍历模块时可自动生成一个从1开始的序号,之后在是把这些信息传递到服务端保存,发送Ajax请求这里就不罗嗦了,使用iframe也行。不过我这里两种方法都没有用,因为用户登陆进后台也可以设置所有模块的栏位和排序,前台我暂只是用Cookie来临时保存所有模块的栏位和排序,不过以后要能在前台拖拽保存所有模块的栏位和排序也很简单了,加个动态页面响应Ajax请求保存所有模块的栏位和排序写入数据库即可。
重要修复:使用添加mousewheel滚动鼠标滑轮事件修正隔空拖拽问题,但还需正确的计算出每次鼠标滑轮的距离。(2010-4-3 17:25)
事例:Drag.html
源码:(Drag.js下载)
Drag.js1/**//*
2** Author : Jonllen
3** Create : 2010-03-21
4** Update : 2010-04-03
5** WebSite: http://www.jonllen.com/
6*/
7
8var Drag = function ( settings ) ...{
9
10 for(p in this.settings)
11 ...{
12 if ( !settings.hasOwnProperty(p) ) settings[p] = this.settings[p];
13 }
14 this.settings = settings;
15
16
17 this.columns = this.settings.column ? this.getElementsByClassName(this.settings.column, this.settings.container, 'div') : document.body.childNodes;
18
19 var _this = this;
20
21 //down
22 settings.container.onmousedown = function (e)...{
23
24 e = window.event || e;
25
26 var handle = e.target || e.srcElement;
27 if( !_this.hasClass(handle, _this.settings.handle) && !_this.hasClass(handle.parentNode, _this.settings.handle) )
28 return;
29
30 handle = _this.hasClass(handle, _this.settings.handle) ? handle : handle.parentNode;
31
32 var mod = handle.parentNode;
33 if( !_this.hasClass(mod, _this.settings.mod) )
34 return;
35
36 mod.style.width = mod.offsetWidth + 'px';
37 mod.style.height = mod.offsetHeight + 'px';
38 mod.style.border = 'dashed 2px #cdcdcd';
39 mod.style.cssText += 'filter :alpha(opacity=80);opacity :0.8;';
40
41 if( _this.getStyle(mod, 'position') != 'absolute' )
42 mod.style.position = 'absolute';
43
44 var y = parseInt(mod.offsetTop) - e.clientY;
45 var x = parseInt(mod.offsetLeft) - e.clientX;
46
47 if(e.preventDefault)...{
48 e.preventDefault();
49 }
50
51 var wheelScrollTop = 0, scrollTop = -document.documentElement.scrollTop;
52 var wheelEvent = function (ev) ...{
53
54 ev = ev || window.event;
55 var direct = 0;
56 if (ev.wheelDelta) ...{
57 direct = ev.wheelDelta;
58 direct += direct > 0 ? -8 : 8;
59 } else if (ev.detail) ...{
60 //ff
61 direct = -ev.detail;
62 direct += direct > 0 ? 48 : -48;
63 }
64
65 wheelScrollTop += -direct;
66 if( wheelScrollTop < scrollTop )
67 ...{
68 wheelScrollTop = scrollTop;
69 }
70
71 document.onmousemove(ev);
72
73 };
74 //fix MouseWheel Event
75 _this.addEvent( document, 'mousewheel', wheelEvent );
76
77 var shadowElem = _this.getDragshow();
78 shadowElem.style.height = mod.offsetHeight + 'px';
79 shadowElem.style.border = 'dashed 2px #ccc';
80 mod.parentNode.insertBefore(shadowElem, mod);
81 mod.style.top = y + e.clientY + "px";
82
83 //move
84 document.onmousemove = function(ev)...{
85
86 ev = window.event || ev;
87
88 log(ev);
89
90 mod.style.top = y + ev.clientY + wheelScrollTop + "px";
91 mod.style.left = x + ev.clientX + "px";
92
93 handle.style.cursor = 'move';
94
95 if( !_this.settings.shadow) return;
96
97 var mousePos = _this.mouseCoords(ev);
98
99 for(var i=0; i< _this.columns.length; i++)...{
100
101 var column = _this.columns[i];
102
103 var columnPos = _this.getPosition(column);
104 var columnWidth = parseInt(column.offsetWidth);
105 var columnHeight = parseInt(column.offsetHeight);
106
107 if( mousePos.x >= columnPos.x && mousePos.x <= (columnPos.x + columnWidth) ) ...{
108
109 var insertBeforeMod = column.lastChild;
110
111 for( var j=0; j<column.childNodes.length; j++)...{
112
113 var modTarget = column.childNodes[j];
114 if( modTarget.nodeType == 1) ...{
115
116 var modTargetPos = _this.getPosition(modTarget);
117 var modTargetWidth = parseInt(modTarget.offsetWidth);
118 var modTargetHeight = parseInt(modTarget.offsetHeight);
119
120 if( (mousePos.y >= modTargetPos.y) && (mousePos.y <= modTargetPos.y + modTargetHeight) ) ...{
121 insertBeforeMod = modTarget;
122 break;
123 }
124 }
125 }
126
127 column.insertBefore(shadowElem, insertBeforeMod);
128 break;
129 }
130
131 }
132
133 window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
134
135 };
136
137 //up
138 document.onmouseup = function(ev)...{
139
140 ev = window.event || ev;
141
142 this.onmousemove = null;
143 this.onmouseup=null;
144
145 handle.style.cssText = '';
146 mod.style.cssText = '';
147
148 if( !_this.settings.shadow) return;
149
150 _this.removeEvent( document, 'mousewheel', wheelEvent );
151
152 var shadowElem = _this.getDragshow();
153 if( shadowElem.parentNode.tagName != 'BODY')
154 shadowElem.parentNode.replaceChild(mod, shadowElem);
155 else shadowElem.parentNode.removeChild(shadowElem);
156
157 _this.save();
158
159 log('save complate!',document.cookie);
160 }
161
162 window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
163 };
164}
165
166Drag.prototype = ...{
167 settings : ...{
168 container : null,
169 column : '',
170 mod : '',
171 handle : '',
172 shadow : true
173 },
174 getStyle : function ( elem, property) ...{
175 if( document.defaultView )
176 ...{
177 var computed = document.defaultView.getComputedStyle(elem, null);
178 return elem.style[property] || computed[property];
179 }
180 return elem.currentStyle[property];
181 },
182 hasClass : function (elem, name)
183 ...{
184 return (' '+elem.className+' ').indexOf(' '+name+' ') != -1
185 },
186 getElementsByClassName : function (className, elem, tagName)
187 ...{
188 elem = elem || document;
189 tagName = tagName || "*";
190 var results = new Array();
191 var elements = elem.getElementsByTagName(tagName);
192 for(var i=0; i<elements.length; i++)
193 ...{
194 var element = elements[i];
195 if( element.className && (' '+element.className+' ').indexOf(' '+className+' ') != -1 )
196 results.push(element);
197 }
198 return results.length > 0 ? results : null;
199 },
200 addEvent : function(target,eventType,func)...{
201 if(target.attachEvent)
202 ...{
203 target.attachEvent("on" + eventType, func);
204
205 }else if(target.addEventListener)
206 ...{
207 target.addEventListener(eventType == 'mousewheel' ? 'DOMMouseScroll' : eventType, func, false);
208 }
209 return this;
210 },
211 removeEvent : function(target,eventType,func)...{
212 if(target.detachEvent)
213 ...{
214 target.detachEvent("on" + eventType, func);
215
216 }else if(target.removeEventListener)
217 ...{
218 target.removeEventListener(eventType == 'mousewheel' ? 'DOMMouseScroll' : eventType, func, false);
219 }
220 return this;
221 },
222 mouseCoords : function (ev)...{
223 if (ev.pageX || ev.pageY) ...{
224 return ...{
225 x: ev.pageX,
226 y: ev.pageY
227 };
228 }
229 return ...{
230 x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
231 y: ev.clientY + document.body.scrollTop - document.body.clientTop
232 };
233 },
234 getPosition : function (elem)...{
235 var left = 0;
236 var top = 0;
237
238 while (elem.offsetParent)...{
239 left += elem.offsetLeft;
240 top += elem.offsetTop;
241 elem = elem.offsetParent;
242 }
243
244 left += elem.offsetLeft;
245 top += elem.offsetTop;
246
247 return ...{x:left, y:top};
248 },
249 getDragshow : function ()...{
250 var id = "shadow";
251 var elem = document.getElementById(id);
252 if( elem == null)
253 ...{
254 elem = document.createElement('div');
255 elem.id = id;
256 elem.className = this.settings.mod + ' ' + id;
257 document.body.insertBefore(elem, document.body.childNodes[0]);
258 }
259 return elem;
260 },save : function () ...{
261 var sort = 1;
262 var cookieArr = new Array();
263 for(var i=0; i<this.columns.length; i++) ...{
264 var column = this.columns[i];
265 for( var j=0; j< column.childNodes.length; j++) ...{
266 var mod = column.childNodes[j];
267 if( mod.nodeType == 1 && this.hasClass(mod, this.settings.mod) ) ...{
268 var columnId = column.className.substring(column.className.length - 1);
269 var modId = mod.id.replace(this.settings.mod,'');
270 cookieArr.push( modId + '=' + columnId + '-' + sort );
271 sort++;
272 }
273 }
274 }
275 this.cookie('Drag',cookieArr.join('&'),10);
276 },cookie : function (name, value, expires)
277 ...{
278 var date = new Date();
279 if( expires )
280 ...{
281 date.setTime(date.getTime() + 1000 * 60 * expires );
282 }
283
284 document.cookie = name + '=' + value + '; path=/; expires=' + ( expires ? date.toGMTString() : '0' );
285 }
286}
287
288function log(obj)
289...{
290 if( typeof(enableLog) == 'undefined' ) return;
291
292 var id = 'log';
293 var logElem = document.getElementById(id);
294 if( logElem == null )
295 ...{
296 logElem = document.createElement('div');
297 logElem.id = id;
298 logElem.style.cssText = 'position:fixed !important;position:absolute;z-index:9999;bottom:2px;left:2px;border:solid 2px #ccc;background-color:#e0e54b;opacity:0.5;filter:alpha(opacity:50);color:red;font-weight:700;overflow-x:hidden;overflow-y:auto;';
299
300 document.body.insertBefore(logElem, document.body.childNodes[0]);
301 }
302 logElem.style.display = 'block';
303 logElem.innerHTML = '';
304 for(var i=0;i < arguments.length; i++)
305 ...{
306 var obj = arguments[i];
307 logElem.innerHTML += '<b>'+obj+'</b><br />';
308 for(var p in obj)
309 ...{
310 logElem.innerHTML += p + ':' + obj[p] + '<br />';
311 }
312 }
313 logElem.style.height = logElem.clientHeight > 500 ? "500px" : "auto";
314
315}
316
317new Drag(...{
318 container : document.getElementById('main'),
319 column : 'column',
320 mod : 'mod',
321 handle : 'head'
322 });
能否给我发个有几个简单asp.net页面的完整例子呢,谢谢啦
crs811@qq.com
若方便,能否将保存数据库的过程也顺便说一下呢,谢谢
谢谢!
email:liulei051610613@126.com
谢谢啦