首先这篇文章是基于 再谈js拖拽(一) code基础上的,进行改动实现的仿iGoogle自定义首页模块拖拽功能。将code贴上,你也可在下方进行下载。code未免枯燥,我将尽量用文字描述思路及注意点,所以即便你不看code也能根据文字翻译成你的code。
1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 <html xmlns="http://www.w3.org/1999/xhtml" >
3 <head>
4 <title></title>
5 <script type="text/javascript">
6 var Common = {
7 getEvent: function() {//ie/ff
8 if (document.all) {
9 return window.event;
10 }
11 func = getEvent.caller;
12 while (func != null) {
13 var arg0 = func.arguments[0];
14 if (arg0) {
15 if ((arg0.constructor == Event || arg0.constructor == MouseEvent) || (typeof (arg0) == "object" && arg0.preventDefault && arg0.stopPropagation)) {
16 return arg0;
17 }
18 }
19 func = func.caller;
20 }
21 return null;
22 },
23 getMousePos: function(ev) {
24 if (!ev) {
25 ev = this.getEvent();
26 }
27 if (ev.pageX || ev.pageY) {
28 return {
29 x: ev.pageX,
30 y: ev.pageY
31 };
32 }
33
34 if (document.documentElement && document.documentElement.scrollTop) {
35 return {
36 x: ev.clientX + document.documentElement.scrollLeft - document.documentElement.clientLeft,
37 y: ev.clientY + document.documentElement.scrollTop - document.documentElement.clientTop
38 };
39 }
40 else if (document.body) {
41 return {
42 x: ev.clientX + document.body.scrollLeft - document.body.clientLeft,
43 y: ev.clientY + document.body.scrollTop - document.body.clientTop
44 };
45 }
46 },
47 getElementPos: function(el) {
48 el = this.getItself(el);
49 var _x = 0, _y = 0;
50 do {
51 _x += el.offsetLeft;
52 _y += el.offsetTop;
53 } while (el = el.offsetParent);
54 return { x: _x, y: _y };
55 },
56 getItself: function(id) {
57 return "string" == typeof id ? document.getElementById(id) : id;
58 },
59 getViewportSize: { w: (window.innerWidth) ? window.innerWidth : (document.documentElement && document.documentElement.clientWidth) ? document.documentElement.clientWidth : (document.body?document.body.offsetWidth:0), h: (window.innerHeight) ? window.innerHeight : (document.documentElement && document.documentElement.clientHeight) ? document.documentElement.clientHeight : (document.body ? document.body.offsetHeight : 0) },
60 isIE: document.all ? true : false,
61 setOuterHtml: function(obj, html) {
62 var Objrange = document.createRange();
63 obj.innerHTML = html;
64 Objrange.selectNodeContents(obj);
65 var frag = Objrange.extractContents();
66 obj.parentNode.insertBefore(frag, obj);
67 obj.parentNode.removeChild(obj);
68 },
69 firstChild: function(parentObj, tagName) {
70 if (Common.isIE) {
71 return parentObj.firstChild;
72 }
73 else {
74 var arr = parentObj.getElementsByTagName(tagName);
75 return arr[0];
76 }
77 },
78 lastChild: function(parentObj, tagName) {
79 if (Common.isIE) {
80 return parentObj.lastChild;
81 }
82 else {
83 var arr = parentObj.getElementsByTagName(tagName);
84 return arr[arr.length - 1];
85 }
86 },
87 setCookie: function(name, value) {
88 document.cookie = name + "=" + value;
89 },
90 getCookie: function(name) {
91 var strCookie = document.cookie;
92 var arrCookie = strCookie.split("; ");
93 for (var i = 0; i < arrCookie.length; i++) {
94 var arr = arrCookie[i].split("=");
95 if (!arr[1]) {
96 return "";
97 }
98 if (arr[0] == name) {
99 return arr[1];
100 }
101 }
102 return "";
103 },
104 delCookie: function(name) {
105 var exp = new Date();
106 exp.setTime(exp.getTime() - 1);
107 var cval = this.getCookie(name);
108 if (cval != null) document.cookie = name + "=" + cval + ";expires=" + exp.toGMTString();
109 }
110 }
111
112 ///------------------------------------------------------------------------------------------------------
113 var Class = {
114 create: function() {
115 return function() { this.init.apply(this, arguments); }
116 }
117 }
118 var Drag = Class.create();
119 Drag.prototype = {
120 init: function(titleBar, dragDiv, Options) {
121 //设置点击是否透明,默认透明60%
122 titleBar = Common.getItself(titleBar);
123 dragDiv = Common.getItself(dragDiv);
124 this.dragArea = { maxLeft: -9999, maxRight: 9999, maxTop: -9999, maxBottom: 9999 };
125 if (Options) {
126 this.opacity = Options.opacity ? (isNaN(parseInt(Options.opacity)) ? 100 : parseInt(Options.opacity)) : 100;
127 if (Options.area) {
128 if (Options.area.left && !isNaN(parseInt(Options.area.left))) { this.dragArea.maxLeft = Options.area.left };
129 if (Options.area.right && !isNaN(parseInt(Options.area.right))) { this.dragArea.maxRight = Options.area.right };
130 if (Options.area.top && !isNaN(parseInt(Options.area.top))) { this.dragArea.maxTop = Options.area.top };
131 if (Options.area.bottom && !isNaN(parseInt(Options.area.bottom))) { this.dragArea.maxBottom = Options.area.bottom };
132 }
133 }
134 else {
135 this.opacity = 60;
136 }
137 this.originDragDiv = null;
138 this.tmpX = 0;
139 this.tmpY = 0;
140 this.moveable = false;
141 this.dragArray = [];
142
143 var dragObj = this;
144 var dragTbl = document.getElementById("dragTable");
145
146 titleBar.onmousedown = function(e) {
147 var ev = e || window.event || Common.getEvent();
148 //只允许通过鼠标左键进行拖拽,IE鼠标左键为1 FireFox为0
149 if (Common.isIE && ev.button == 1 || !Common.isIE && ev.button == ground-color: #f5f5f5; color: #000000;">0) {
150 }
151 else {
152 return false;
153 }
154
155
156 //处理特殊情况:在最上/下面MOVE时不碰到现有DIV的情况下,又回到起始拖拽的列最上/下方
157 var tmpColId;
158 for (c = 0; c < dragTbl.rows[0].cells.length; c++) {
159 for (k = 0; k < dragTbl.rows[0].cells[c].getElementsByTagName("DIV").length; k++) {
160 if (dragDiv.id == dragTbl.rows[0].cells[c].getElementsByTagName("DIV")[k].id) {
161 tmpColId = c;
162 break;
163 }
164 }
165 }
166 var tmpPosFirstChild = Common.getElementPos(Common.firstChild(dragTbl.rows[0].cells[tmpColId], "DIV"));
167 var tmpPosLastChild = Common.getElementPos(Common.lastChild(dragTbl.rows[0].cells[tmpColId], "DIV"));
168 var tmpObj = { colId: tmpColId, firstChildUp: tmpPosFirstChild.y, lastChildDown: tmpPosLastChild.y + Common.lastChild(dragTbl.rows[0].cells[tmpColId], "DIV").offsetHeight };
169
170 //保存当前可拖拽各容器的所在位置
171 dragObj.dragArray = dragObj.RegDragsPos();
172
173 //插入虚线框
174 var dashedElement = document.createElement("div");
175 dashedElement.style.cssText = dragDiv.style.cssText;
176 dashedElement.style.border = " dashed 2px #aaa ";
177 dashedElement.style.marginBottom = "6px";
178 dashedElement.style.width = dragDiv.offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px"; //减去boderWidth使虚线框大小保持与dragDiv一致
179 dashedElement.style.height = dragDiv.offsetHeight - 2 * parseInt(dashedElement.style.borderWidth) + "px"; //加上px 保证FF正确
180 dashedElement.style.position = "relative";
181 if (dragDiv.nextSibling) {
182 dragDiv.parentNode.insertBefore(dashedElement, dragDiv.nextSibling);
183 }
184 else {
185 dragDiv.parentNode.appendChild(dashedElement);
186 }
187 //拖动时变为absolute
188 dragDiv.style.width = dragDiv.offsetWidth + "px";
189 dragDiv.style.position = "absolute";
190
191
192 dragObj.moveable = true;
193 dragDiv.style.zIndex = dragObj.GetZindex() + 1;
194
195 var downPos = Common.getMousePos(ev);
196 dragObj.tmpX = downPos.x - dragDiv.offsetLeft;
197 dragObj.tmpY = downPos.y - dragDiv.offsetTop;
198
199 if (Common.isIE) {
200 dragDiv.setCapture();
201 } else {
202 window.captureEvents(Event.mousemove);
203 }
204
205 dragObj.SetOpacity(dragDiv, dragObj.opacity);
206
207 //FireFox 去除容器内拖拽图片问题
208 if (ev.preventDefault) {
209 ev.preventDefault();
210 ev.stopPropagation();
211 }
212
213 document.onmousemove = function(e) {
214 if (dragObj.moveable) {
215 var ev = e || window.event ||t;background-color: #f5f5f5; color: #000000;"> Common.getEvent();
216 //IE 去除容器内拖拽图片问题
217 if (document.all) //IE
218 {
219 ev.returnValue = false;
220 }
221
222 var movePos = Common.getMousePos(ev);
223 dragDiv.style.left = Math.max(Math.min(movePos.x - dragObj.tmpX, dragObj.dragArea.maxRight), dragObj.dragArea.maxLeft) + "px";
224 dragDiv.style.top = Math.max(Math.min(movePos.y - dragObj.tmpY, dragObj.dragArea.maxBottom), dragObj.dragArea.maxTop) + "px";
225
226 var targetDiv = null;
227 for (var k = 0; k < dragObj.dragArray.length; k++) {
228 if (dragDiv == dragObj.dragArray[i]) {
229 continue;
230 }
231
232 if (movePos.x > dragObj.dragArray[k].PosLeft && movePos.x < dragObj.dragArray[k].PosLeft + dragObj.dragArray[k].PosWidth
233 && movePos.y > dragObj.dragArray[k].PosTop && movePos.y < dragObj.dragArray[k].PosTop + dragObj.dragArray[k].PosHeight
234 ) {
235 targetDiv = document.getElementById(dragObj.dragArray[k].DragId);
236 if (movePos.y < dragObj.dragArray[k].PosTop + dragObj.dragArray[k].PosHeight / 2) {
237 //往上移
238 dashedElement.style.width = targetDiv.offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px";
239 targetDiv.parentNode.insertBefore(dashedElement, targetDiv);
240 }
241 else {
242 //往下移
243 dashedElement.style.width = targetDiv.offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px";
244 if (targetDiv.nextSibling) {
245 targetDiv.parentNode.insertBefore(dashedElement, targetDiv.nextSibling);
246 }
247 else {
248 targetDiv.parentNode.appendChild(dashedElement);
249 }
250 }
251 }
252 }
253
254 for (j = 0; j < dragTbl.rows[0].cells.length; j++) {
255 var startLeft = Common.getElementPos(dragTbl.rows[0].cells[j]).x;
256 if (movePos.x > startLeft && movePos.x < startLeft + dragTbl.rows[0].cells[j].offsetWidth) {
257 ///列无DIV
258 if (dragTbl.rows[0].cells[j].getElementsByTagName("div").length == 0) {
259 dashedElement.style.width = dragTbl.rows[0].cells[j].offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px";
260 dragTbl.rows[0].cells[j].appendChild(dashedElement);
261 }
262 else {
263 var posFirstChild = Common.getElementPos(Common.firstChild(dragTbl.rows[0].cells[j], "DIV"));
264 var posLastChild = Common.getElementPos(Common.lastChild(dragTbl.rows[0].cells[j], "DIV"));
265 //处理特殊情况:在最上/下面MOVE时不碰到现有DIV的情况下,又回到起始拖拽的列最上/下方
266 var tmpUp, tmpDown;
267 if (tmpObj.colId == j) {
268 tmpUp = tmpObj.firstChildUp;
269 tmpDown = tmpObj.lastChildDown;
270 }
271 else {
272 tmpUp = posFirstChild.y;
273 tmpDown = posLastChild.y + Common.lastChild(dragTbl.rows[0].cells[j], "DIV").offsetHeight;
274 }
275
276 if (movePos.y < tmpUp) {///从最上面插入虚线框
277 dashedElement.style.width = Common.firstChild(dragTbl.rows[0].cells[j], "DIV").offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px";
278 dragTbl.rows[0].cells[j].insertBefore(dashedElement, Common.firstChild(dragTbl.rows[0].cells[j], "DIV"));
279 }
280 else if (movePos.y > tmpDown) {///从最下面插入虚线框
281 dashedElement.style.width = Common.lastChild(dragTbl.rows[0].cells[j], "DIV").offsetWidth - 2 * parseInt(dashedElement.style.borderWidth) + "px";
282 dragTbl.rows[0].cells[j].appendChild(dashedElement);
283 }
284
285 }
286 }
287 }
288 }
289 };
290
291 document.onmouseup = function() {
292 if (dragObj.moveable) {
293 if (Common.isIE) {
294 dragDiv.releaseCapture();
295 }
296 else {
297 window.releaseEvents(dragDiv.mousemove);
298 }
299 dragObj.SetOpacity(dragDiv, 100);
300 dragObj.moveable = false;
301 dragObj.tmpX = 0;
302 dragObj.tmpY = 0;
303
304 //务必写在此IF内
305 dragDiv.style.left = "";
306 dragDiv.style.top = "";
307 dragDiv.style.width = "";
308 dragDiv.style.position = "";
309 dashedElement.parentNode.insertBefore(dragDiv, dashedElement);
310 dashedElement.parentNode.removeChild(dashedElement);
311 }
312
313 };
314
315 }
316 },
317 SetOpacity: function(dragDiv, n) {
318 if (Common.isIE) {
319 dragDiv.filters.alpha.opacity = n;
320 }
321 else {
322 dragDiv.style.opacity = n / 100;
323 }
324
325 },
326 GetZindex: function() {
327 var maxZindex = 0;
328 var divs = document.getElementsByTagName("div");
329 for (z = 0; z < divs.length; z++) {
330 maxZindex = Math.max(maxZindex, divs[z].style.zIndex);
331 }
332 return maxZindex;
333 },
334 RegDragsPos: function() {
335 var arrDragDivs = new Array();
336 var dragTbl = document.getElementById("dragTable");
337 var tmpDiv, tmpPos;
338 for (i = 0; i < dragTbl.getElementsByTagName("div").length; i++) {
339 tmpDiv = dragTbl.getElementsByTagName("div")[i];
340 if (tmpDiv.className = "dragDiv") {
341 tmpPos = Common.getElementPos(tmpDiv);
342 arrDragDivs.push({ DragId: tmpDiv.id, PosLeft: tmpPos.x, PosTop: tmpPos.y, PosWidth: tmpDiv.offsetWidth, PosHeight: tmpDiv.offsetHeight });
343 }
344 }
345 return arrDragDivs;
346 }
347 }
348
349 window.onload = function() {
350 var dragTbl = document.getElementById("dragTable");
351 if (Common.getCookie("configArr")) {
352 var configArr = eval("(" + Common.getCookie("configArr") + ")");
353 for (i = 0; i < dragTbl.rows[0].cells.length; i++) {
354 for (j = 0; j < configArr[i].length; j++) {
355 dragTbl.rows[0].cells[i].appendChild(document.getElementById(configArr[i][j]));
356 }
357 }
358 }
359 new Drag("titleBar1", "dragDiv1");
360 new Drag("titleBar2", "dragDiv2");
361 new Drag("titleBar3", "dragDiv3");
362 new Drag("titleBar4", "dragDiv4");
363 new Drag("titleBar5", "dragDiv5");
364 }
365
366 window.onunload = function() {
367 var dragTbl = document.getElementById("dragTable");
368 var configArr = "";
369 for (i = 0; i < dragTbl.rows[0].cells.length; i++) {
370 var tmpStr = "";
371 for (j = 0; j < dragTbl.rows[0].cells[i].getElementsByTagName("DIV").length; j++) {
372 tmpStr += (tmpStr == "" ? "" : ",") + "'" + dragTbl.rows[0].cells[i].getElementsByTagName("DIV")[j].id + "'";
373 }
374 configArr += (configArr == "" ? "" : ",") + "[" + tmpStr + "]";
375 }
376 //format like: [['dragDiv3','dragDiv5'],['dragDiv4','dragDiv1'],['dragDiv2']]
377 Common.setCookie("configArr", "[" + configArr + "]");
378 }
379 </script>
380 <style type="text/css">
381 .spanDiv{
382 position:relative;
383 width:5px;
384 height:5px;
385 }
386
387 .dragDiv,.nodragDiv{
388 position:relative;
389 filter:alpha(opacity=100);
390 opacity:1;
391 margin-bottom:6px;
392 background-color:#FFFFFF;
393 }
394 </style>
395 </head>
396 <body >
397 <script type="text/javascript">
398
399 </script>
400 <table id="dragTable" cellpadding="3" style=" border:solid 0px green;width:98%;">
401 <tr>
402 <td valign="top" style="width:30%">
403 <div class="dragDiv" id="dragDiv1" >
404 <table cellpadding="0" cellspacing="0" border="1" style="width:100%;border-collapse:collapse; border-color:Blue">
405 <tr id="titleBar1" style="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;cursor:move;">
406 <th align="left" unselectable="on" >Title1</th>
407 </tr>
408 <tr style="height:130px;padding:3px;" align="left" valign="top" >
409 <td unselectable="on">Content1...</td>
410 </tr>
411 </table>
412 </div>
413 <div class="dragDiv" id="dragDiv2">
414 <table cellpadding="0" cellspacing="0" border="1" style="width:100%;border-collapse:collapse; border-color:Blue">
415 <tr id="titleBar2" style="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;cursor:move;">
416 <th align="left" unselectable="on" >Title2</th>
417 </tr>
418 <tr style="height:130px;padding:3px;" align="left" valign="top" >
419 <td unselectable="on">Content2...</td>
420 </tr>
421 </table>
422 </div>
423 </td>
424 <td valign="top" style="width:50%">
425 <div class="dragDiv" id="dragDiv3">
426 <table cellpadding="0" cellspacing="0" border="1" style="width:100%;border-collapse:collapse; border-color:Blue">
427 <tr id="titleBar3" style="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;cursor:move;">
428 <th align="left" unselectable="on" >Title3</th>
429 </tr>
430 <tr style="height:230px;padding:3px;" align="left" valign="top" >
431 <td unselectable="on">Content3...</td>
432 </tr>
433 </table>
434 </div>
435 </td>
436 <td valign="top" style="width:20%">
437 <div class="dragDiv" id="dragDiv4">
438 <table cellpadding="0" cellspacing="0" border="1" style="width:100%;border-collapse:collapse; border-color:Blue">
439 <tr id="titleBar4" style="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;cursor:move;">
440 <th align="left" unselectable="on" >Title4</th>
441 </tr>
442 <tr style="height:130px;padding:3px;" align="left" valign="top" >
443 <td unselectable="on">Content4...</td>
444 </tr>
445 </table>
446 </div>
447 <div class="dragDiv" id="dragDiv5">
448 <table cellpadding="0" cellspacing="0" border="1" style="width:100%;border-collapse:collapse; border-color:Blue">
449 <tr id="titleBar5" style="height:22px; text-align:left; background-color:#547BC9;color:White; padding:3px;cursor:move;">
450 <th align="left" unselectable="on" >Title5</th>
451 </tr>
452 <tr style="height:130px;padding:3px;" align="left" valign="top" >
453 <td unselectable="on">Content5...</td>
454 </tr>
455 </table>
456 </div>
457 </td>
458 </tr>
459 </table>
460 </body>
461 </html>
462
1. 准备
1.1 由于模块拖拽中使用虚线框,所以去除(一)中拖拽的保持原有DIV的功能(即keepOrigin);
1.2 在新建Drag对象时如未设置拖拽时透明值,默认改为60%的透明度;
1.3 将拖拽范围上下左右默认设置为9999,因为模块拖拽超过当前可视窗口是必要的。
1.4 在(一)中有个BUG,即用IE第一次打开页面报缺少对象的错,如要重现,右键文件选择IE打开,刷新后或者将文件拖到已有IE浏览器中无法重现。原因Common.getViewportSize中有document.body.offsetWidth,IE中第一次打开,当解析到这句时,document.body不存在,所以取offsetWidth时错误。所以要么把这段放到body标签中,要么进行对document.body的判断。
1.5 HTML结构:这里使用的是Table,在TD中存放要拖拽的DIV。当然也可以全部用DIV布局。但是拖拽的DIV的postion要设为relative,因为他是在文档流中的,absolute则是脱离文档流。
2. 思路及注意点
2.1 在mouseDown时,插入虚线框DIV,这里要注意设置虚线框的position为relative,插入到当前拖拽DIV所处列的文档流中,同时把当前拖拽DIV的position改为absolute,使脱离文档流来进行拖拽。虚线框DIV的宽度要注意减去2倍的boderWidth,否则虚线框将撑大当前列,造成文档布局移位。同时,保证插入虚线框DIV的code要在赋当前拖拽DIV的ZIndex值前面,以保证当前拖拽DIV的ZIndex最大。
2.2 在mouseUp时,插入当前拖拽DIV至虚线框前,将当前拖拽DIV的position、left、top、width清空,进入文档流。然后删除虚线框。
2.3 模块自定义拖拽的核心操作即在mouseMove时。
2.3.1 首先是虚线框的高度宽度处理,我的处理是高度保持不变,宽度取所移动到当前列的宽度。
2.3.2 然后核心问题是:什么时候让虚线框移动到什么位置。
我的处理是:拿当前拖拽中的鼠标位置与页面可拖拽的各DIV的绝对位置进行比较。所以在mouseDown时还要记住当前页面可拖拽DIV的绝对位置和宽度高度(RegDragsPos方法中),存入数组。
当拖拽中鼠标进入某DIV区域内,如在该DIV上半部,则虚线框插到该DIV的上方,如在下半部则插在下方。
当拖拽中鼠标并未触及某具体DIV区域内,在外部游离时,则首先判断处于那一列范围内,然后如果该列没有可拖拽DIV,则虚线框直接插入该列;若该列有DIV,则判断鼠标位置处于该列第一个DIV上方,则往上插,处于该列最下面DIV下方,则往下插。至此所有可能情况均处理。
有一特殊情况,譬如,当前拖拽DIV在最上方游离,鼠标不碰及任何DIV,从第一列开始,移动到第二第三列虚线框插入均正常,但是回到第一列时发现无法插入。因为此时当前拖拽的DIV仍然作为第一列的第一个DIV,在判断是否位于该列第一个DIV上方时不符合条件无法执行虚线框插入的操作。所以在mouseDown时临时记下该列第一个DIV的top和最后一个DIV 下沿绝对位置,以此比较。
对于什么时候让虚线框移动到什么位置,IGoogle以前版本的处理是:找到取距离当前鼠标位置这点最近的可拖拽DIV(取最短的两点间距离),然后让虚线框插入该DIV处。现在版本不清楚怎么做的。
3.拖拽之后保存当前模块布局,使刷新页面仍保持自定义的模块布局。
其实是在window.onunload中用cookie记下当前布局状态,如: [['dragDiv3','dragDiv5'],['dragDiv4','dragDiv1'],['dragDiv2']] 。window.onload中去布局。实际应用中可以将此插入后台数据库进行保存。
over了,貌似啰嗦啊,哎。接下来我将写关于js动画特效的系列,给自己布置点任务先。
作者:JayChow
出处:http://ljchow.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,请在文章页面明显位置给出原文链接。
联系客服