js应用-实现博客个性主页布局拖拽功能
Jquery的Interface elements for jQuery里面的拖拽布局存在一些bug,效率也比较低,GoogleUI google_drag.js有些乱,不是很容易理解,Discuz!NT Space代码满天飞,所以自己参考GoogleUI的思想,简化和优化了一些操作代码,实现了博客系统基本的拖拽布局的效果,暂时未考虑其他浏览器的兼容性问题。下一步准备改造成Jquery的插件形式,并增加一些渐隐渐现和动画效果,并逐步实现一些ajax的添加删除操作,嵌入基于JQuery的音乐播放器,图片浏览器,文本编辑器。
预览体验:
html代码:
下面的可拖拽模块的mid为其在数据库中的id号;
<div style="display:inline" mid="|"><div></div></div>
每td列最后都有一个,并隐藏起来,用来可以推拽元素到此隐藏元素的前面,或者某td列本来没有元素,
也可以拖拽到此列上面:
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<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
5<title>博客推拽布局示例</title>
6<link href="main.css" rel="stylesheet" type="text/css" />
7<script src="drag.js" language="javascript"></script>
8</head>
9
10<body>
11<div id="modules">
12 <table id="main" cellspacing="10" border="0" width="98%" align="center">
13 <tr>
14 <td id="c1">
15 <div class="module" mid="1">
16 <div class="title">title1</div>
17 <div class="content">content1</div>
18 </div>
19 <div class="module" mid="4">
20 <div class="title">title4</div>
21 <div class="content">content4</div>
22 </div>
23 <div style="display:inline" mid="|"><div></div></div>
24 </td>
25 <td id="c2" >
26 <div class="module" mid="2">
27 <div class="title">title2</div>
28 <div class="content">content2</div>
29 </div>
30 <div style="display:inline" mid="|"><div></div></div>
31 </td>
32 <td id="c3" >
33 <div class="module" mid="3">
34 <div class="title">title3</div>
35 <div class="content">content3</div>
36 </div>
37 <div style="display:inline" mid="|"><div></div></div>
38 </td>
39 </tr>
40 </table>
41 <div id="ghost"></div>
42</div>
43布局顺序为:<span id="order" />
44<script>
45 //实例化一个dragLayout对象
46 var dragObj = new guozili.dragLayout({
47 targetId: "main",
48 //dragArray为拖拽完后新的dragModule对象
49 onEnd: function(dragArray) {
50 var order = "";
51 for(var i in dragArray)
52 {
53 order += dragArray[i].ele.getAttribute("mid") + " ";
54 }
55
56 getElementById("order").innerText = order;
57 //或者进行ajax提交
58 }
59
60 });
61
62</script>
63</body>
64</html>
2<html xmlns="http://www.w3.org/1999/xhtml">
3<head>
4<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
5<title>博客推拽布局示例</title>
6<link href="main.css" rel="stylesheet" type="text/css" />
7<script src="drag.js" language="javascript"></script>
8</head>
9
10<body>
11<div id="modules">
12 <table id="main" cellspacing="10" border="0" width="98%" align="center">
13 <tr>
14 <td id="c1">
15 <div class="module" mid="1">
16 <div class="title">title1</div>
17 <div class="content">content1</div>
18 </div>
19 <div class="module" mid="4">
20 <div class="title">title4</div>
21 <div class="content">content4</div>
22 </div>
23 <div style="display:inline" mid="|"><div></div></div>
24 </td>
25 <td id="c2" >
26 <div class="module" mid="2">
27 <div class="title">title2</div>
28 <div class="content">content2</div>
29 </div>
30 <div style="display:inline" mid="|"><div></div></div>
31 </td>
32 <td id="c3" >
33 <div class="module" mid="3">
34 <div class="title">title3</div>
35 <div class="content">content3</div>
36 </div>
37 <div style="display:inline" mid="|"><div></div></div>
38 </td>
39 </tr>
40 </table>
41 <div id="ghost"></div>
42</div>
43布局顺序为:<span id="order" />
44<script>
45 //实例化一个dragLayout对象
46 var dragObj = new guozili.dragLayout({
47 targetId: "main",
48 //dragArray为拖拽完后新的dragModule对象
49 onEnd: function(dragArray) {
50 var order = "";
51 for(var i in dragArray)
52 {
53 order += dragArray[i].ele.getAttribute("mid") + " ";
54 }
55
56 getElementById("order").innerText = order;
57 //或者进行ajax提交
58 }
59
60 });
61
62</script>
63</body>
64</html>
js代码:
主要是两个对象,dragLayout对象(table元素) 包含 dragModule对象(可拖拽的元素)
1 if (typeof getElementById!="function") {
2 var getElementById = function (id) {
3 if (typeof(id)=="object") return id;
4 if (document.getElementById(id)) { return document.getElementById(id); }
5 else { throw new Error(id +" argument error, can not find \"" +id+ "\" element"); }
6 }
7 }
8 // 获取一个element的offset信息,其实就是相对于Body的padding以内的绝对坐标
9 function getElCoordinate (e) {
10 var t = e.offsetTop;
11 var l = e.offsetLeft;
12 var w = e.offsetWidth;
13 var h = e.offsetHeight;
14 while (e=e.offsetParent) {
15 t += e.offsetTop;
16 l += e.offsetLeft;
17 }; return {
18 top: t,
19 left: l,
20 width: w,
21 height: h,
22 bottom: t+h,
23 right: l+w
24 }
25 }
26
27 var guozili = window.guozili || {};
28 //整个table布局对象
29 guozili.dragLayout = function(cfg) {
30 this.targetId = cfg.targetId;
31 //推拽完成时的回调函数,可以进行ajax提交
32 this.onEnd = cfg.onEnd;
33 this.init.apply(this);
34 };
35
36 guozili.dragLayout.prototype = {
37 //初始化,读取每列下面的推拽模块div,并且放入dragArray数组中
38 init : function() { with(this) {
39 target = getElementById(this.targetId);
40 rows = target.tBodies[0].rows[0];
41 column = rows.cells;
42 this.dragArray = new Array();
43 var counter = 0;
44 for (var i = 0; i < column.length; i ++ ) {
45 var ele = column[i];
46
47 for( var j = 0; j < ele.childNodes.length; j ++ ) {
48 var ele1 = ele.childNodes[j];
49 if (ele1.tagName == "DIV" && ele1.getAttribute("mid")) {
50 dragArray[counter] = new guozili.dragModule(ele1, this);
51 counter++ ;
52 }
53 }
54
55 }
56 }
57 }
58 };
59 //拖拽模块div对象
60 guozili.dragModule = function(ele, parent) {
61 //对应的div拖拽元素
62 this.ele = ele;
63 //父对象,即dragLayout对象
64 this.parent = parent;
65 //标题栏,用于鼠标拖拽
66 this.title = this.ele.childNodes[0];
67 //计算拖拽element的坐标
68 this.eleLeft = getElCoordinate(this.ele).left;
69 this.eleTop = getElCoordinate(this.ele).top;
70 //记录原先的邻居节点,用来对比是否被移动到新的位置
71 this.origNextSibling = ele.nextSibling;
72 this.init.apply(this);
73 };
74
75 guozili.dragModule.prototype = {
76 init : function() { with(this) {
77 var _self = this;
78 // 获取移动的时候那个灰色的虚线框
79 ghostLayer = getElementById("ghost");
80 //鼠标按下时推拽开始
81 title.onmousedown = function (event) {
82 _self.dragStart(event);
83 }
84 title.style.cursor = "move";
85
86 }
87 },
88 //开始拖拽设定一些位置信息
89 dragStart: function (evt) { with(this) {
90 var _self = this;
91 evt = evt?evt:window.event;
92
93 var postion = getElCoordinate(ele)
94 //鼠标相对于浏览器的位置减去元素的位置
95 //得出鼠标相对于元素的相对位置,便于拖拽时计算元素的新位置
96 x = evt.clientX - postion.left;
97 y = evt.clientY - postion.top;
98
99 //绝对位置,top和left就起作用了,就可以推拽了
100 ele.style.position = "absolute";
101 ele.style.top = postion.top;
102 ele.style.left = postion.left;
103 ele.style.zIndex = 100;
104
105 //将那个灰框设定得与正在拖动的对象一样高
106 ghostLayer.style.position = "relative";
107 ghostLayer.style.display = "block";
108 ghostLayer.style.height = postion.height;
109 ghostLayer.style.width = postion.width;
110 //把灰框放到这个对象原先的位置上
111 ele.parentNode.insertBefore(ghostLayer, ele.nextSibling);
112
113 //鼠标按下再移动的事件,鼠标移动,元素也跟着走
114 document.onmousemove = function (event) { _self.drag(event); }
115 //释放鼠标的事件
116 document.onmouseup = function (event) { _self.dragEnd(event); }
117 }
118 },
119 //拖拽时实现元素跟鼠标走
120 drag: function (evt) { with(this) {
121 var _self = this;
122 evt = evt?evt:window.event;
123 //计算元素的新的位置
124 ele.style.left = evt.clientX - x;
125 ele.style.top = evt.clientY - y;
126 ele.style.filter = "alpha(opacity=70)" ;
127 ele.style.opacity = 0.7 ;
128 //被拖拽到的新的元素(当然也可以是原来那个)
129 var found = null;
130 //最大的距离
131 var max_distance = 10000;
132 // 遍历所有的可拖拽的element,寻找离当前鼠标坐标最近的那个可拖拽元素,以便前面插入
133 for (var i = 0; i < parent.dragArray.length; i++)
134 {
135 var dragObj = parent.dragArray[i];
136 //利用勾股定理计算鼠标到遍历到的这个元素的距离
137 var distance = Math.sqrt(Math.pow(evt.clientX - dragObj.eleLeft,2) + Math.pow(evt.clientY - dragObj.eleTop, 2));
138
139 if (isNaN(distance)){
140 continue ;
141 }
142 //如果更小,记录下这个距离,并将它作为found
143 if (distance < max_distance) {
144 max_distance = distance;
145 found = dragObj;
146 }
147
148
149 }
150 //找到落脚点就先把灰框插进去,我们看到的那个灰框停靠的特效
151 if (found != null && ghostLayer.nextSibling != found.ele) {
152 found.ele.parentNode.insertBefore(ghostLayer, found.ele);
153
154 }
155
156
157 }
158 },
159 //鼠标释放时推拽完成
160 dragEnd: function (evt) { with(this) {
161 var _self = this;
162 evt = evt?evt:window.event;
163
164 document.onmousemove = null;
165 document.onmouseup = null;
166 //把拖拽时的position=absolute和相关的那些style都消除
167 ele.style.position = "relative";
168 ele.style.filter = "";
169 ele.style.opacity = "";
170 ele.style.zIndex = "";
171 ele.style.left = "";
172 ele.style.top = "";
173 //将灰框隐藏起来
174 ghostLayer.style.display = "none";
175
176 //如果现在的邻居不是原来的邻居了后者邻居就是它本身
177 if (ghostLayer.nextSibling != origNextSibling && ghostLayer.nextSibling != this.ele) {
178 //把被拖拽的这个节点插到灰框的前面
179 ghostLayer.parentNode.insertBefore(ele, ghostLayer.nextSibling);
180 //从新初始化可推拽元素对象,可以设定它们的新位置,为下面的拖拽操作做准备
181 parent.dragArray = null;
182 parent.init();
183 //回调函数,拖拽完成可对dragArray进行处理
184 parent.onEnd.call(this, parent.dragArray);
185
186 }
187
188
189
190
191
192 }
193 }
194
195
196 };
2 var getElementById = function (id) {
3 if (typeof(id)=="object") return id;
4 if (document.getElementById(id)) { return document.getElementById(id); }
5 else { throw new Error(id +" argument error, can not find \"" +id+ "\" element"); }
6 }
7 }
8 // 获取一个element的offset信息,其实就是相对于Body的padding以内的绝对坐标
9 function getElCoordinate (e) {
10 var t = e.offsetTop;
11 var l = e.offsetLeft;
12 var w = e.offsetWidth;
13 var h = e.offsetHeight;
14 while (e=e.offsetParent) {
15 t += e.offsetTop;
16 l += e.offsetLeft;
17 }; return {
18 top: t,
19 left: l,
20 width: w,
21 height: h,
22 bottom: t+h,
23 right: l+w
24 }
25 }
26
27 var guozili = window.guozili || {};
28 //整个table布局对象
29 guozili.dragLayout = function(cfg) {
30 this.targetId = cfg.targetId;
31 //推拽完成时的回调函数,可以进行ajax提交
32 this.onEnd = cfg.onEnd;
33 this.init.apply(this);
34 };
35
36 guozili.dragLayout.prototype = {
37 //初始化,读取每列下面的推拽模块div,并且放入dragArray数组中
38 init : function() { with(this) {
39 target = getElementById(this.targetId);
40 rows = target.tBodies[0].rows[0];
41 column = rows.cells;
42 this.dragArray = new Array();
43 var counter = 0;
44 for (var i = 0; i < column.length; i ++ ) {
45 var ele = column[i];
46
47 for( var j = 0; j < ele.childNodes.length; j ++ ) {
48 var ele1 = ele.childNodes[j];
49 if (ele1.tagName == "DIV" && ele1.getAttribute("mid")) {
50 dragArray[counter] = new guozili.dragModule(ele1, this);
51 counter++ ;
52 }
53 }
54
55 }
56 }
57 }
58 };
59 //拖拽模块div对象
60 guozili.dragModule = function(ele, parent) {
61 //对应的div拖拽元素
62 this.ele = ele;
63 //父对象,即dragLayout对象
64 this.parent = parent;
65 //标题栏,用于鼠标拖拽
66 this.title = this.ele.childNodes[0];
67 //计算拖拽element的坐标
68 this.eleLeft = getElCoordinate(this.ele).left;
69 this.eleTop = getElCoordinate(this.ele).top;
70 //记录原先的邻居节点,用来对比是否被移动到新的位置
71 this.origNextSibling = ele.nextSibling;
72 this.init.apply(this);
73 };
74
75 guozili.dragModule.prototype = {
76 init : function() { with(this) {
77 var _self = this;
78 // 获取移动的时候那个灰色的虚线框
79 ghostLayer = getElementById("ghost");
80 //鼠标按下时推拽开始
81 title.onmousedown = function (event) {
82 _self.dragStart(event);
83 }
84 title.style.cursor = "move";
85
86 }
87 },
88 //开始拖拽设定一些位置信息
89 dragStart: function (evt) { with(this) {
90 var _self = this;
91 evt = evt?evt:window.event;
92
93 var postion = getElCoordinate(ele)
94 //鼠标相对于浏览器的位置减去元素的位置
95 //得出鼠标相对于元素的相对位置,便于拖拽时计算元素的新位置
96 x = evt.clientX - postion.left;
97 y = evt.clientY - postion.top;
98
99 //绝对位置,top和left就起作用了,就可以推拽了
100 ele.style.position = "absolute";
101 ele.style.top = postion.top;
102 ele.style.left = postion.left;
103 ele.style.zIndex = 100;
104
105 //将那个灰框设定得与正在拖动的对象一样高
106 ghostLayer.style.position = "relative";
107 ghostLayer.style.display = "block";
108 ghostLayer.style.height = postion.height;
109 ghostLayer.style.width = postion.width;
110 //把灰框放到这个对象原先的位置上
111 ele.parentNode.insertBefore(ghostLayer, ele.nextSibling);
112
113 //鼠标按下再移动的事件,鼠标移动,元素也跟着走
114 document.onmousemove = function (event) { _self.drag(event); }
115 //释放鼠标的事件
116 document.onmouseup = function (event) { _self.dragEnd(event); }
117 }
118 },
119 //拖拽时实现元素跟鼠标走
120 drag: function (evt) { with(this) {
121 var _self = this;
122 evt = evt?evt:window.event;
123 //计算元素的新的位置
124 ele.style.left = evt.clientX - x;
125 ele.style.top = evt.clientY - y;
126 ele.style.filter = "alpha(opacity=70)" ;
127 ele.style.opacity = 0.7 ;
128 //被拖拽到的新的元素(当然也可以是原来那个)
129 var found = null;
130 //最大的距离
131 var max_distance = 10000;
132 // 遍历所有的可拖拽的element,寻找离当前鼠标坐标最近的那个可拖拽元素,以便前面插入
133 for (var i = 0; i < parent.dragArray.length; i++)
134 {
135 var dragObj = parent.dragArray[i];
136 //利用勾股定理计算鼠标到遍历到的这个元素的距离
137 var distance = Math.sqrt(Math.pow(evt.clientX - dragObj.eleLeft,2) + Math.pow(evt.clientY - dragObj.eleTop, 2));
138
139 if (isNaN(distance)){
140 continue ;
141 }
142 //如果更小,记录下这个距离,并将它作为found
143 if (distance < max_distance) {
144 max_distance = distance;
145 found = dragObj;
146 }
147
148
149 }
150 //找到落脚点就先把灰框插进去,我们看到的那个灰框停靠的特效
151 if (found != null && ghostLayer.nextSibling != found.ele) {
152 found.ele.parentNode.insertBefore(ghostLayer, found.ele);
153
154 }
155
156
157 }
158 },
159 //鼠标释放时推拽完成
160 dragEnd: function (evt) { with(this) {
161 var _self = this;
162 evt = evt?evt:window.event;
163
164 document.onmousemove = null;
165 document.onmouseup = null;
166 //把拖拽时的position=absolute和相关的那些style都消除
167 ele.style.position = "relative";
168 ele.style.filter = "";
169 ele.style.opacity = "";
170 ele.style.zIndex = "";
171 ele.style.left = "";
172 ele.style.top = "";
173 //将灰框隐藏起来
174 ghostLayer.style.display = "none";
175
176 //如果现在的邻居不是原来的邻居了后者邻居就是它本身
177 if (ghostLayer.nextSibling != origNextSibling && ghostLayer.nextSibling != this.ele) {
178 //把被拖拽的这个节点插到灰框的前面
179 ghostLayer.parentNode.insertBefore(ele, ghostLayer.nextSibling);
180 //从新初始化可推拽元素对象,可以设定它们的新位置,为下面的拖拽操作做准备
181 parent.dragArray = null;
182 parent.init();
183 //回调函数,拖拽完成可对dragArray进行处理
184 parent.onEnd.call(this, parent.dragArray);
185
186 }
187
188
189
190
191
192 }
193 }
194
195
196 };
css代码:
1body {
2font-size:12px;
3}
4
5#main {
6TABLE-LAYOUT:fixed; border:1px solid #ccc;
7}
8
9#main td {
10VERTICAL-ALIGN: top; WIDTH: 32%
11}
12
13.module {
14width:100%;
15position:relative;
16border:1px solid #ccc;
17margin-bottom:10px;
18}
19
20.module .title {
21border-top:5px solid #ccc;
22background-color:#f5f5f5;
23font-size:13px;
24color:#990000;
25width:100%;
26}
27
28.module .content {
29padding:5px;
30}
31
32.block {
33width:1px; height:1px; position:relative; overflow:hidden;
34}
35
36#ghost {
37border:2px dashed #990000;
38position:absolute;
39display:none;
40top:0px;
41left:0px;
42margin-bottom:10px;
43}
2font-size:12px;
3}
4
5#main {
6TABLE-LAYOUT:fixed; border:1px solid #ccc;
7}
8
9#main td {
10VERTICAL-ALIGN: top; WIDTH: 32%
11}
12
13.module {
14width:100%;
15position:relative;
16border:1px solid #ccc;
17margin-bottom:10px;
18}
19
20.module .title {
21border-top:5px solid #ccc;
22background-color:#f5f5f5;
23font-size:13px;
24color:#990000;
25width:100%;
26}
27
28.module .content {
29padding:5px;
30}
31
32.block {
33width:1px; height:1px; position:relative; overflow:hidden;
34}
35
36#ghost {
37border:2px dashed #990000;
38position:absolute;
39display:none;
40top:0px;
41left:0px;
42margin-bottom:10px;
43}