实现手柄拖动的功能,就是把mousedown的事件侦听器放到handle中。由于我们原先程序的骨架搭建得比较好,添加新功能非常容易。
1.
#
2.
(handle || el).onmousedown = dragstart;
但这不够人性化,最好是我们输入一个类名作为handle的参数,拖动类会自动根据此类名在其子孙元素寻找此元素。
01.
if
(handle){
02.
var
cls =
new
RegExp(
"(^|\\s)"
+ handle +
"(\\s|$)"
);
03.
for
(
var
i=0,l=el.childNodes.length;i <l;i++){
04.
var
child = el.childNodes[i];
05.
if
(child.nodeType == 1 && cls.test(child.className)){
06.
_handle = child;
07.
break
;
08.
}
09.
}
10.
}
相应地方修改为:
1.
#
2.
(_handle || el).onmousedown = dragstart;
有时如果拖动元素是个非常复杂,包含相当多东西,这样拖起来很吃内存,这时是不是拖动一个空元素是不是好一些呢?我们可以克隆原来的拖动元素,并把 它加入原来元素的后面(相同z-Index,后面的会放到前面的前面)。如果要求是用手柄拖动,我们把原来的手柄也克隆过来就是。
01.
if
(ghosting){
// ghosting为可选参数,表示使用克隆体拖动
02.
_ghost = el.cloneNode(
false
);
03.
el.parentNode.insertBefore(_ghost,el.nextSibling);
04.
if
(_handle){
05.
_handle = _handle.cloneNode(
false
);
06.
_ghost.appendChild(_handle);
07.
}
08.
!+
"\v1"
? _ghost.style.filter =
"alpha(opacity=50)"
: _ghost.style.opacity = 0.5;
09.
}
我们还可以增添一些回调函数(onStart,onDrag,onEnd),如果用户没有实现回调函数,我们给个空函数(function(){})它就好了。
像拖动时显示元素的坐标,我们也可以做成可选参数coords。默认为true,并在手柄上显示,但这样会覆盖住原来的一些东西,我们在拖动前把它保存到一个变量中,拖动后再还原。
另,如果是一个范围中拖动,如一个指定的容器或浏览器的可视区,当拖动元素存在margin时,其右边与下边可能会超出容器。因此我们必须取出其相 应的margin,当右边与下边的坐标大于容器右边或下边的坐标时,强逼元素往左边与上边移动相应的值,那个值不用说就是margin值。我们用一个简单 的getStyle()函数来取margin值,因此设置元素的margin时请以px为单位,否则在IE中会计算错误。
01.
var
getStyle =
function
(el, style){
02.
if
(!+
"\v1"
){
03.
style = style.replace(/\-(\w)/g,
function
(all, letter){
04.
return
letter.toUpperCase();
05.
});
06.
var
value = el.currentStyle[style];
07.
(value ==
"auto"
)&&(value =
"0px"
);
08.
return
value;
09.
}
else
{
10.
return
document.defaultView.getComputedStyle(el,
null
).getPropertyValue(style)
11.
}
12.
}
最后一个较为重要的功能,当元素往右边或下边移动,让元素永远留在可视区。这怎样做到呢?我们计算元素右下角的坐标(right与bottom), 然后再计算出浏览器可视区的宽与高,当right大于宽时,就让它们的差值作为浏览器的scrollLeft,bottom的情形相仿。
01.
if
(scroll){
//表示让滚动条与元素一起移动
02.
var
doc = isQuirk ? document.body : document.documentElement;
03.
doc = options.container || doc;
04.
var
a = getCoords(el).left + el.offsetWidth;
05.
var
b = doc.clientWidth;
06.
if
(a > b){
07.
doc.scrollLeft += a - b;
08.
}
09.
var
c = getCoords(el).top + el.offsetHeight;
10.
var
d = doc.clientHeight;
11.
if
(c > d){
12.
doc.scrollTop += c - d;
13.
}
14.
}
最后函数扩展成以下样子,功能比较多,下附文档说明:
001.
//辅助函数1
002.
var
getCoords =
function
(el){
003.
var
box = el.getBoundingClientRect(),
004.
doc = el.ownerDocument,
005.
body = doc.body,
006.
html = doc.documentElement,
007.
clientTop = html.clientTop || body.clientTop || 0,
008.
clientLeft = html.clientLeft || body.clientLeft || 0,
009.
top = box.top + (self.pageYOffset || html.scrollTop || body.scrollTop ) - clientTop,
010.
left = box.left + (self.pageXOffset || html.scrollLeft || body.scrollLeft) - clientLeft
011.
return
{
'top'
: top,
'left'
: left };
012.
};
013.
//辅助函数2
014.
var
getStyle =
function
(el, style){
015.
if
(!+
"\v1"
){
016.
style = style.replace(/\-(\w)/g,
function
(all, letter){
017.
return
letter.toUpperCase();
018.
});
019.
var
value = el.currentStyle[style];
020.
(value ==
"auto"
)&&(value =
"0px"
);
021.
return
value;
022.
}
else
{
023.
return
document.defaultView.getComputedStyle(el,
null
).getPropertyValue(style)
024.
}
025.
}
026.
//============================
027.
var
Drag =
function
(id){
028.
var
el = document.getElementById(id),
029.
isQuirk = document.documentMode ? document.documentMode == 5 : document.compatMode && document.compatMode !=
"CSS1Compat"
,
030.
options = arguments[1] || {},
031.
container = options.container || document.documentElement,
032.
limit = options.limit,
033.
lockX = options.lockX,
034.
lockY = options.lockY,
035.
ghosting = options.ghosting,
036.
handle = options.handle,
037.
revert = options.revert,
038.
scroll = options.scroll,
039.
coords =
true
|| options.coords,
040.
onStart = options.onStart ||
function
(){},
041.
onDrag = options.onDrag ||
function
(){},
042.
onEnd = options.onEnd ||
function
(){} ,
043.
marginLeft = parseFloat(getStyle(el,
"margin-left"
)),
044.
marginRight = parseFloat(getStyle(el,
"margin-right"
)),
045.
marginTop = parseFloat(getStyle(el,
"margin-top"
)),
046.
marginBottom = parseFloat(getStyle(el,
"margin-bottom"
)),
047.
cls,
048.
_handle,
049.
_ghost,
050.
_top,
051.
_left,
052.
_html;
053.
el.lockX = getCoords(el).left;
054.
el.lockY = getCoords(el).top;
055.
el.style.position =
"absolute"
;
056.
if
(handle){
057.
cls =
new
RegExp(
"(^|\\s)"
+ handle +
"(\\s|$)"
);
058.
for
(
var
i=0,l=el.childNodes.length;i<l;i++){
059.
var
child = el.childNodes[i];
060.
if
(child.nodeType == 1 && cls.test(child.className)){
061.
_handle = child;
062.
break
;
063.
}
064.
}
065.
}
066.
_html = (_handle || el).innerHTML;
067.
var
dragstart =
function
(e){
068.
e = e || window.event;
069.
el.offset_x = e.clientX - el.offsetLeft;
070.
el.offset_y = e.clientY - el.offsetTop;
071.
document.onmouseup = dragend;
072.
document.onmousemove = drag;
073.
if
(ghosting){
074.
_ghost = el.cloneNode(
false
);
075.
el.parentNode.insertBefore(_ghost,el.nextSibling);
076.
if
(_handle){
077.
_handle = _handle.cloneNode(
false
);
078.
_ghost.appendChild(_handle);
079.
}
080.
!+
"\v1"
? _ghost.style.filter =
"alpha(opacity=50)"
: _ghost.style.opacity = 0.5;
081.
}
082.
(_ghost || el).style.zIndex = ++Drag.z;
083.
onStart();
084.
return
false
;
085.
}
086.
var
drag =
function
(e) {
087.
e = e || window.event;
088.
el.style.cursor =
"pointer"
;
089.
!+
"\v1"
? document.selection.empty() : window.getSelection().removeAllRanges();
090.
_left = e.clientX - el.offset_x ;
091.
_top = e.clientY - el.offset_y;
092.
if
(scroll){
093.
var
doc = isQuirk ? document.body : document.documentElement;
094.
doc = options.container || doc;
095.
options.container && (options.container.style.overflow =
"auto"
);
096.
var
a = getCoords(el).left + el.offsetWidth;
097.
var
b = doc.clientWidth;
098.
if
(a > b){
099.
doc.scrollLeft = a - b;
100.
}
101.
var
c = getCoords(el).top + el.offsetHeight;
102.
var
d = doc.clientHeight;
103.
if
(c > d){
104.
doc.scrollTop = c - d;
105.
}
106.
}
107.
if
(limit){
108.
var
_right = _left + el.offsetWidth ,
109.
_bottom = _top + el.offsetHeight,
110.
_cCoords = getCoords(container),
111.
_cLeft = _cCoords.left,
112.
_cTop = _cCoords.top,
113.
_cRight = _cLeft + container.clientWidth,
114.
_cBottom = _cTop + container.clientHeight;
115.
_left = Math.max(_left, _cLeft);
116.
_top = Math.max(_top, _cTop);
117.
if
(_right > _cRight){
118.
_left = _cRight - el.offsetWidth - marginLeft - marginRight;
119.
}
120.
if
(_bottom > _cBottom){
121.
_top = _cBottom - el.offsetHeight - marginTop - marginBottom;
122.
}
123.
}
124.
lockX && ( _left = el.lockX);
125.
lockY && ( _top = el.lockY);
126.
(_ghost || el).style.left = _left +
"px"
;
127.
(_ghost || el).style.top = _top +
"px"
;
128.
coords && _ghost && ((_handle || _ghost).innerHTML = _left +
" x "
+ _top);
129.
onDrag();
130.
}
131.
132.
var
dragend =
function
(){
133.
document.onmouseup =
null
;
134.
document.onmousemove =
null
;
135.
_ghost && el.parentNode.removeChild(_ghost);
136.
el.style.left = _left +
"px"
;
137.
el.style.top = _top +
"px"
;
138.
(_handle || el).innerHTML = _html;
139.
if
(revert){
140.
el.style.left = el.lockX +
"px"
;
141.
el.style.top = el.lockY +
"px"
;
142.
}
143.
onEnd();
144.
}
145.
Drag.z = 999;
146.
(_handle || el).onmousedown = dragstart;
147.
}
参数 | 类型 | 说明 |
---|---|---|
id | string | 必选,拖动元素的ID |
container | object | 可拖的范围,必须为拖动元素的父级元素,是否为定位元素无所谓。 |
limit | boolean | 默认false,与container配合使用。当值为true时,它会以container或浏览器的可视区作拖动范围。 |
lockX | boolean | 默认false。当值为true时,锁定X轴,只允许上下移动。 |
lockY | boolean | 默认false。当值为true时,锁定Y轴,只允许左右移动。 |
handle | string | 手柄的类名,当设置了此参数后,只允许用手柄拖动元素。 |
ghosting | boolean | 默认false。当值为true时,会生成一个与拖动元素相仿的影子元素,拖动时只拖动影子元素,以减轻内存消耗。 |
revert | boolean | 默认false。当值为true时,让拖动元素在拖动后回到原来的位置。 |
coords | boolean | 默认true。拖动时在手柄或影子元素上显示元素的坐标。 |
scroll | boolean | 默认false。当值为true时,允许滚动条随拖动元素移动。 |
onStart | function | 在拖动前鼠标按下的那一瞬执行。 |
onDrag | function | 在拖动时执行。 |
onEnd | function | 在拖动后鼠标弹起的那一瞬执行。 |