在一个拖拽系统中,做出来容易细节,细节却并不尽如人意..拖拽中没有一个完美(或者说比较完美)的解决方案,原因在于:
我们所知道拖拽的实现的方法(cross-browser)是有三个事件的:onmousedown,onmousemove,onmouseup,
即onmousedown来捕获,onmousemove拖拽,onmouseup释放事件. 这在一般情况下是没有问题的.在此种情况下会出现事件没有释放------当鼠标按下(onmousedown)移动某element(onmousemove)到IE(或其它浏览器)窗体的时候再松开鼠标,这个时候onmouseup并没有释放事件,你再将鼠标移入浏览器窗体时,element会拖动. 此细节可描述为onmouseup在浏览器外的时候,浏览器无法触发该事件.
今天和JK讨论这个问题的时候, 有了一个解决方法, 在这里记录也算是一个自己的笔记. 问题2以后再写了,如果你有比较好的解决方案,麻烦将思路Email一份给我,Thnaks:) blueDestiny[at]126.com, 请将[at]替换成@.
首先, 请点击以下链接,按照问题描述的那样操作,看看是否属实:
http://www.never-online.net/code/js/dragdemo/
解决方案:
IE下,可以采用setCapture()方法,在onmousedown时捕获,在onmouseup时释放, 由此可以推出setCapture是"全局"(这个全局不是指全局变量中的全局,而是在整个窗体区域)的.看MS给的例子:
<BODY onload="oOwnCapture.setCapture()"
onclick="document.releaseCapture()">
<DIV ID=oOwnCapture
onmousemove="oWriteLocation.value = event.x + event.y";
onlosecapture="alert(event.srcElement.id +
' lost mouse capture.')">
<P>Mouse capture has been set to this gray division (DIV)
at load time using the setCapture method. The text area will
track the mousemove event through the <B>x</B>
and <B>y</B> properties of the event object.<BR>
<P>Event bubbling works as usual on objects within a
container that has mouse capture. Demonstrate this concept by
clicking the button below or changing the active window from
this one, and then back. After oOwnCapture loses mouse capture,
the text area continues tracking the mousemove events only
while the cursor is over objects it contains.</P>
<BR><BR>
<TEXTAREA ID=oWriteLocation COLS=2>
mouse location</TEXTAREA>
</DIV>
<HR>
<DIV ID=oNoCapture>
<P><a href="http://www.never-online.net">www.never-online.net, by JK, Rank, never-online</P>
<INPUT VALUE="Move mouse over this object.">
<INPUT TYPE=button VALUE="Click to End Mouse Capture">
</DIV>
</BODY>
再者非IE浏览器(如Firefox)如何是好,FF也有window.captureEvents(Event.MOUSEDOWN)这种类似的方法,不过和IE里面的效果差别有点大(不知道是不是我用错了的原因,总之,感觉IE底下的capture比FF好得多). 在写这篇文章之前,调试代码调试了很久...JK的代码思路和我的一模一样,但是我的在Firefox里跑的时候总是丢掉mouseup事件,经过我不断的精简代码,发现....
1.在FF里用禁止事件的默认返回值,(event.preventDefault())会使onmouseup事件丢失...
2.在我原来的代码里,我为了在拖动时不给用户选择,在IE里用了onselectstart,实际上用了setCapture时,也就是禁止了在拖动时选择...,在FF里我用了一个CSS:-moz-user-select:none;,也就是这个CSS,导致在firefox里onmouseup丢失.动态的加载style即MozUserSelect为none是无效的.
还有就是在拖动时,假如capture了某个对象,在拖动时可以无限制的拖动,显示这是不合理的,习惯的范围应该是 鼠标>=0 && 鼠标<=可视文档范围,即
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Rank's HTML document</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-Control" content="no-cache" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="ImageToolbar" content="no" />
<style type="text/css" title="default" media="screen">
/*<![CDATA[*/
/*]]>*/
</style>
</head>
<body>
<div id="demo">鼠标按下,并拖动,可看到参数 <a href="http://www.never-online.net">never-online, rank</a></div>
<script type="text/javascript">
//<![CDATA[
function getDocRect (wnd) {
wnd = wnd || window;
doc = wnd.document;
return {
left :doc.documentElement.scrollLeft||doc.body.scrollLeft||wnd.pageXOffset||wnd.scrollX||0,
top :doc.documentElement.scrollTop||doc.body.scrollTop||wnd.pageYOffset||wnd.scrollY||0,
width :doc.documentElement.clientWidth||doc.body.clientWidth||wnd.innerWidth||doc.width||0,
height :doc.documentElement.clientHeight||doc.body.clientHeight||wnd.innerHeight||doc.height||0
}
};
var c = 0; var b = false;
var d = document.getElementById("demo");
document.onmousedown = function () {
d.innerHTML = 'down'; b = true;
if (d.setCapture) d.setCapture();
document.onmousemove = function (evt) {
if (!b) return;
evt = evt || window.event;
var x, y;
var rect = getDocRect();
x = evt.clientX;
y = evt.clientY;
window.defaultStatus = 'x:' +x+ ',y:' +y+ ' -- w:' +rect.width+ ',h:' +rect.height+ ',l:' +rect.left+ ',t:' +rect.top;
if (x>=rect.width || y>=rect.height
|| 0>=x || 0>=y) {
return;
}
d.innerHTML = c++;
};
document.onmouseup = function () {
if (!b) return;
if (document.releaseCapture) document.releaseCapture();
document.onmousemove = null;
d.innerHTML = 'up';
b = false;
}
}
//]]>
</script>
</body>
</html>
还有一个细节
就是Firefox里当你要拖动的容器里的内容(如文本)已经选中时,点击选中区域拖动鼠标(鼠标的icon在FF里显示时非法操作),再进行拖动,mouseup也会丢失,解决此问题的方法有几种:
1.拖动时始终将focus指向另外一个element,可使拖动的容器不选中.
2.拖动结束后将focus指向另外一个element
3.取消range中所有选择.
第二个解决方法比较好,而且只用focus一次.
再有一个细节
因为event(event.clientX和event.clientY)和position(即元素坐标)不属于一个坐标系. 因此当出现滚动条时要在编程时手动调整过来, 思路是计算scrollNumber的偏移值,在移动的时候加到滚动的偏移值.但是对这个仍然不是一个满意的答案,因为在某些情况下,显得拖动的滚动条出现(不出现)很不自然....
完整代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> drag demo change size or change layout - http://www.never-online.net </title>
<meta http-equiv="ImageToolbar" content="no" />
<meta name="author" content="never-online, BlueDestiny"/>
<meta name="keywords" content="drag, custom layout, never modules, Mozilla CSS, C#, .net, Reference, BlueDestiny, never-online"/>
<meta name="description" content="drag demo change size or change layout, javascript reference, c sharp artilces"/>
<meta name="creator.name" content="never-online, BlueDestiny" />
<style type="text/css" media="all" title="Default">
@import "/code/js/dragdemo/main.css";
div#demoDrag { cursor:move; position:absolute; border:1px double #000; background-color:buttonface; width:200px; height:100px; color:#CC0000; text-align:center;}
</style>
</head>
<body id="www.never-online.net">
<div id="header">
<h1> drag demo change size or change layout </h1>
by JK, never-online, <a href="http://www.never-online.net">www.never-online.net</a>
<hr/>
</div>
<div class="wrapper">
<div class="content">
<input type="radio" value="size" name="custom" id="size"/>
<label for="size">拖动改变尺寸</label>
<input type="radio" value="layout" name="custom" id="layout" checked/>
<label for="layout">拖动改变布局</label>
</div>
</div>
<div id="demoDrag" onmousedown="handlestart(event, this)">Easy to write the drag code, <br/>this is a simple demo, <br/>by never-online,<br/> http://www.never-online.net</div>
<script type="text/javascript">
//<![CDATA[
function getAbsolutePosition (e) {
var width = e.offsetWidth;
var height = e.offsetHeight;
var left = e.offsetLeft;
var top = e.offsetTop;
while (e=e.offsetParent) {
left += e.offsetLeft;
top += e.offsetTop;
};
var right = left+width;
var bottom = top+height;
return {
'width': width, 'height': height,
'left': left, 'top': top,
'right': right, 'bottom': bottom
}
};
function getDocRect (wnd) {
wnd = wnd || window;
doc = wnd.document;
return {
left :doc.documentElement.scrollLeft||doc.body.scrollLeft||wnd.pageXOffset||wnd.scrollX||0+10,
top :doc.documentElement.scrollTop||doc.body.scrollTop||wnd.pageYOffset||wnd.scrollY||0,
width :doc.documentElement.clientWidth||doc.body.clientWidth||wnd.innerWidth||doc.width||0,
height :doc.documentElement.clientHeight||doc.body.clientHeight||wnd.innerHeight||doc.height||0
}
};
var elDrag = null;
var bLayout = true;
var isIE = !!window.ActiveXObject;
var bDraging = false;
var handlestart = function (evt, el) {
var rect = getDocRect();
var p = getAbsolutePosition(el);
bLayout = document.getElementById('layout').checked?true:false;
bDraging = true;
evt = window.event||evt;
elDrag = el;
if (elDrag.setCapture) elDrag.setCapture();
elDrag.onlosecapture = function() { handlestop(); }
elDrag.deltaX = evt.clientX+rect.left-(bLayout?p.left:p.width);
elDrag.deltaY = evt.clientY+rect.top-(bLayout?p.top:p.height);
};
var handledraging = function (evt) {
if (!bDraging) return false;
evt = window.event||evt;
try {
var rect = getDocRect();
var x, y;
x = evt.clientX+rect.left-elDrag.deltaX;
y = evt.clientY+rect.top-elDrag.deltaY;
if (bLayout) {
if (evt.clientX>1 && evt.clientX<=rect.width)
elDrag.style.left = x +'px';
if (evt.clientY>1 && evt.clientY<=rect.height)
elDrag.style.top = y +'px';
} else {
if (x>1 && evt.clientX<=rect.width)
elDrag.style.width = x +'px';
if (y>1 && evt.clientY<=rect.height)
elDrag.style.height = y +"px";
}
} catch (ex) {};
};
var handlestop = function (evt) {
evt = evt || window.event;
if (!bDraging) return false;
if (elDrag.releaseCapture) elDrag.releaseCapture();
document.body.focus();
bDraging = false;
};
document.onmousemove = handledraging;
document.onmouseup = handlestop;
//]]>
</script>
</body>
</html>
来源:http://www.never-online.net/blog/article.asp?id=192