欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript 拖放效果代碼

 更新時間:2008年12月10日 17:36:08   作者:  
拖放效果,也叫拖拽、拖動,學(xué)名Drag-and-drop ,是最常見的js特效之一。 如果忽略很多細(xì)節(jié),實(shí)現(xiàn)起來很簡單,但往往細(xì)節(jié)才是難點(diǎn)所在。

這個程序的原型是在做圖片切割效果的時候做出來的,那時參考了好幾個同類的效果,跟muxrwc和BlueDestiny學(xué)習(xí)了不少東西。
雖然每次整理都覺得很好了,不過每隔一段時間又會發(fā)現(xiàn)得某個地方可以改善,某個地方有錯誤,某些需求需要實(shí)現(xiàn),就像自己學(xué)習(xí)的知識那樣。
這里考慮到有的人可能只需要簡單的拖放,所以有一個簡化版的拖放SimpleDrag,方便學(xué)習(xí)。
效果預(yù)覽
ps:在maxthon下如果開啟廣告過濾的話很可能會被過濾掉(不知有什么方法可以避免)。
程序說明
【程序原理】
這里以SimpleDrag為例說一下基本原理。
首先初始化程序中要一個拖放對象:

復(fù)制代碼 代碼如下:

this.Drag = $(drag);

還要兩個參數(shù)在開始時記錄鼠標(biāo)相對拖放對象的坐標(biāo):
復(fù)制代碼 代碼如下:

this._x = this._y = 0;

還有兩個事件對象函數(shù)用于添加移除事件:
復(fù)制代碼 代碼如下:

this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);

分別是拖動程序和停止拖動程序。
拖放對象的position必須是absolute絕對定位:
復(fù)制代碼 代碼如下:

this.Drag.style.position = "absolute";

最后把Start開始拖放程序綁定到拖放對象mousedown事件:
addEventHandler(this.Drag, "mousedown", BindAsEventListener(this, this.Start));
鼠標(biāo)在拖放對象按住,就會觸發(fā)Start程序,主要是用來準(zhǔn)備拖動,在這里記錄鼠標(biāo)相對拖放對象的坐標(biāo):
復(fù)制代碼 代碼如下:

this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;

并把_fM拖動程序和_fS停止拖動程序分別綁定到document的mousemove和mouseup事件:
復(fù)制代碼 代碼如下:

addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, "mouseup", this._fS);

注意要綁定到document才可以保證事件在整個窗口文檔中都有效,如果只綁定到拖放對象就很容易出現(xiàn)拖太快就脫節(jié)的現(xiàn)象。
當(dāng)鼠標(biāo)在文檔上移動時,就會觸發(fā)Move程序了,這里就是實(shí)現(xiàn)拖動的程序。
通過現(xiàn)在鼠標(biāo)的坐標(biāo)值跟開始拖動時鼠標(biāo)相對的坐標(biāo)值的差就可以得到拖放對象應(yīng)該設(shè)置的left和top了:
this.Drag.style.left = oEvent.clientX - this._x + "px";
this.Drag.style.top = oEvent.clientY - this._y + "px";
最后放開鼠標(biāo)后就觸發(fā)Stop程序結(jié)束拖放。
這里的主要作用是把Start程序中給document添加的事件移除:
removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, "mouseup", this._fS);
這樣一個簡單的拖放程序就做好了,下面說說其他擴(kuò)展和細(xì)節(jié)部分。
【拖放鎖定】
鎖定分三種,分別是:水平方向鎖定(LockX)、垂直方向鎖定(LockY)、完全鎖定(Lock)。
這個比較簡單,水平和垂直方向的鎖定只要在Move判斷是否鎖定再設(shè)置left和top就行,如果是完全鎖定就直接返回。
if(!this.LockX){ this.Drag.style.left = ; }
if(!this.LockY){ this.Drag.style.top = ; }
【觸發(fā)對象】
觸發(fā)對象是用來觸發(fā)拖放程序的,程序中通過Handle屬性設(shè)置。有的時候不需要整個拖放對象都用來觸發(fā),這時就需要觸發(fā)對象了。
使用了觸發(fā)對象后,進(jìn)行移動的還是拖放對象,只是用觸發(fā)對象來觸發(fā)拖放(一般的使用是把觸發(fā)對象放到拖放對象里面)。
ps:觸發(fā)對象的另一個用法是通過設(shè)置相同的Handle,實(shí)現(xiàn)一個觸發(fā)對象同時拖放多個拖放對象。
【范圍限制】
要設(shè)置范圍限制必須先把Limit設(shè)為true。范圍限制分兩種,分別是固定范圍和容器范圍限制,主要在Move程序中設(shè)置。
原理是當(dāng)比較的值超過范圍時,修正left和top要設(shè)置的值使拖放對象能保持在設(shè)置的范圍內(nèi)。
【固定范圍限制】
容器范圍限制就是指定上下左右的拖放范圍。
各個屬性的意思是:
上(mxTop):top限制;
下(mxBottom):top+offsetHeight限制;
左(mxLeft):left限制;
右(mxRight):left+offsetWidth限制。
如果范圍設(shè)置不正確,可能導(dǎo)致上下或左右同時超過范圍的情況,程序中有一個Repair程序用來修正范圍參數(shù)的。
Repair程序會在程序初始化和Start程序中執(zhí)行,在Repair程序中修正mxRight和mxBottom:
this.mxRight = Math.max(this.mxRight, this.mxLeft + this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);
其中mxLeft+offsetWidth和mxTop+offsetHeight分別是mxRight和mxBottom的最小范圍值。
根據(jù)范圍參數(shù)修正移動參數(shù):
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft);
iTop = Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);
對于左邊上邊要取更大的值,對于右邊下面就要取更小的值。
【容器范圍限制】
容器范圍限制的意思就是把范圍限制在一個容器_mxContainer內(nèi)。
要注意的是拖放對象必須包含在_mxContainer中,因?yàn)槌绦蛑惺鞘褂孟鄬Χㄎ粊碓O(shè)置容器范圍限制的(如果是在容器外就要用絕對定位,這樣處理就比較麻煩了),還有就是容器空間要比拖放對象大,這個就不用說明了吧。
原理跟固定范圍限制差不多,只是范圍參數(shù)是根據(jù)容器的屬性的設(shè)置的。
當(dāng)設(shè)置了容器,在Repair程序會自動把position設(shè)為relative來相對定位:
!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || (this._mxContainer.style.position = "relative");
ps:其中CurrentStyle是用來獲取最終樣式(詳細(xì)看這里的最終樣式部分)。
注意如果在程序執(zhí)行之前設(shè)置過拖放對象的left和top而容器沒有設(shè)置relative,在自動設(shè)置relative時會發(fā)生移位現(xiàn)象,所以程序在初始化時就執(zhí)行一次Repair程序防止這種情況。因?yàn)閛ffsetLeft和offsetTop要在設(shè)置relative之前獲取才能正確獲取值,所以在Start程序中Repair要在設(shè)置_x和_y之前執(zhí)行。
由于是相對定位,對于容器范圍來說范圍參數(shù)上下左右的值分別是0、clientHeight、0、clientWidth。
clientWidth和clientHeight是容器可視部分的寬度和高度(詳細(xì)參考這里)。
為了容器范圍能兼容固定范圍的參數(shù),程序中會獲取容器范圍和固定范圍中范圍更小的值:
Code
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth);
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);
因?yàn)樵O(shè)置相對定位的關(guān)系,容器_mxContainer設(shè)置過后一般不要取消或修改,否則很容易造成移位異常。
【鼠標(biāo)捕獲】
我在一個拖放實(shí)例中看到,即使鼠標(biāo)移動到瀏覽器外面,拖放程序依然能夠執(zhí)行,仔細(xì)查看后發(fā)現(xiàn)是用了setCapture。
鼠標(biāo)捕獲(setCapture)是這個程序的重點(diǎn),作用是將鼠標(biāo)事件捕獲到當(dāng)前文檔的指定的對象。這個對象會為當(dāng)前應(yīng)用程序或整個系統(tǒng)接收所有鼠標(biāo)事件。
使用很簡單:
this._Handle.setCapture();
setCapture捕獲以下鼠標(biāo)事件:onmousedown、onmouseup、onmousemove、onclick、ondblclick、onmouseover和onmouseout。
程序中主要是要捕獲onmousemove和onmouseup事件。
msdn的介紹中還說到setCapture有一個bool參數(shù),用來設(shè)置在容器內(nèi)的鼠標(biāo)事件是否都被容器捕獲。
容器就是指調(diào)用setCapture的對象,大概意思就是:
參數(shù)為true時(默認(rèn))容器會捕獲容器內(nèi)所有對象的鼠標(biāo)事件,即容器內(nèi)的對象不會觸發(fā)鼠標(biāo)事件(跟容器外的對象一樣);
參數(shù)為false時容器不會捕獲容器內(nèi)對象的鼠標(biāo)事件,即容器內(nèi)的對象可以正常地觸發(fā)事件和取消冒泡。
而對于容器外的鼠標(biāo)事件無論參數(shù)是什么都會被捕獲,可以用下面這個簡單的例子測試一下(ie):
Code
<html>
<body onclick="alert(2)">
<div onmousemove="alert(1)">mouseover</div>
<script>document.body.setCapture();</script>
</body>
</html>
這里的參數(shù)是true,一開始body會捕獲所有鼠標(biāo)事件,即使鼠標(biāo)經(jīng)過div也不會觸發(fā)onmousemove事件。
換成false的話,div就可以捕獲鼠標(biāo)事件,就能觸發(fā)div的onmousemove事件了。
拖放結(jié)束后還要使用releaseCapture釋放鼠標(biāo),這個可以放在Stop程序中:
this._Handle.releaseCapture();
setCapture是ie的鼠標(biāo)捕獲方法,對于ff也有對應(yīng)的captureEvents和releaseEvents方法。
但這兩個方法只能由window來調(diào)用,而且muxrwc說這兩個方法在DOM2里已經(jīng)廢棄了,在ff里已經(jīng)沒用了。
不過ff里貌似會自動設(shè)置取消鼠標(biāo)捕獲,但具體的情形就不清楚了,找不到一個比較詳細(xì)的介紹,誰有這方面的資料記得告訴我啊。
下面都是我的猜測,ff的鼠標(biāo)捕獲相當(dāng)于能自動設(shè)置和釋放的document.body.setCapture(false)。
因?yàn)槲覝y試下面的程序,發(fā)現(xiàn)ie和ff效果是差不多的:
ie:
Code
<html>
<body>
<div id="aa" onmouseover="alert(1)"></div>
<script>
document.body.onmousedown=function(){this.setCapture(false)}
document.body.onmouseup=function(){this.releaseCapture()}
document.onmousemove=function(){aa.innerHTML+=1}
</script>
</body>
</html>
ff:
Code
<html>
<body>
<div id="aa" onmouseover="alert(1)"></div>
<script>
document.onmousemove=function(){aa.innerHTML+=1}
</script>
</body>
</html>
可惜沒有權(quán)威的資料參考就只能猜猜了,還有很多還沒有理解的地方以后再研究拉。
注意ff2下的鼠標(biāo)捕獲有一個bug,當(dāng)拖放對象內(nèi)部沒有文本內(nèi)容并拖放到瀏覽器外時捕獲就會失效。
給拖放對象插入一個空文本,例如<font size='1px'>&nbsp;</font>就可以解決,不過這個bug在ff3已經(jīng)修正了。
【焦點(diǎn)丟失】
一般情況下,鼠標(biāo)捕獲都能正常捕獲事件,但如果瀏覽器窗口的焦點(diǎn)丟失就會導(dǎo)致捕獲失效。
我暫時測試到會導(dǎo)致焦點(diǎn)丟失的操作包括切換窗口(包括alt+tab),alert和popup等彈出窗體。
當(dāng)焦點(diǎn)丟失時應(yīng)該同時執(zhí)行Stop程序結(jié)束拖放,但當(dāng)焦點(diǎn)丟失就不能捕獲mouseup事件也就是不能觸發(fā)_fS。
還好ie有onlosecapture事件會在捕獲失效時觸發(fā),針對這個情況可以這樣設(shè)置:
addEventHandler(this._Handle, "losecapture", this._fS);
并在Stop程序中移除:
removeEventHandler(this._Handle, "losecapture", this._fS);
但ff沒有類似的方法,不過muxrwc找到一個替代losecapture的window.onblur事件,那么可以在Start程序中設(shè)置:
addEventHandler(window, "blur", this._fS);
在Stop程序中移除:
removeEventHandler(window, "blur", this._fS);
那ie也有window.onblur事件,用window.onblur代替losecapture不就可以省一段代碼了嗎。
接著我做了一些測試,發(fā)現(xiàn)基本上觸發(fā)losecapture的情況都會同時觸發(fā)window.onblur,看來真的可以。
于是我修改程序用window.onblur代替losecapture,但測試后就出問題了,我發(fā)現(xiàn)如果我用alt+tab切換到另一個窗口,拖動還可以繼續(xù),但這個時候應(yīng)該是已經(jīng)丟失焦點(diǎn)了。
于是我逐一排除測試和程序代碼,結(jié)果發(fā)現(xiàn)如果使用了DTD,那么window.onblur會在再次獲得焦點(diǎn)時才會觸發(fā)。
大家可以用下面這段代碼測試:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script>window.onblur=function(){alert(1)}</script>
在切換到其他程序后,再切換回來才會觸發(fā)window.onblur,還有幾個比較怪異的狀況就不說了,反正ie用window.onblur是不理想的了。
【取消默認(rèn)動作】
對選擇狀態(tài)的文本內(nèi)容、連接和圖片等進(jìn)行拖放操作會觸發(fā)系統(tǒng)的默認(rèn)動作,例如ie中拖動圖片鼠標(biāo)會變成禁止操作狀態(tài),這樣會導(dǎo)致這個拖放程序執(zhí)行失敗。
不過ie在設(shè)置了setCapture之后,通過用戶界面用鼠標(biāo)進(jìn)行拖放操作和內(nèi)容選擇都會被禁止。
意思就是setCapture之后就不能對文檔內(nèi)容進(jìn)行拖放和選擇,注意這里的拖放是指系統(tǒng)的默認(rèn)動作,例如ondragstart就不會被觸發(fā)。
不過如果setCapture的參數(shù)是false的話,容器內(nèi)的對象還是可以觸發(fā)事件的(具體看鼠標(biāo)捕獲部分),所以setCapture的參數(shù)要設(shè)成true或保留默認(rèn)值。
而ff的鼠標(biāo)捕獲沒有這個功能,但可以用preventDefault來取消事件的默認(rèn)動作來解決:
oEvent.preventDefault();
ps:據(jù)說使用preventDefault會出現(xiàn)mouseup丟失的情況,但我在ff3中測試沒有發(fā)現(xiàn),如果各位發(fā)現(xiàn)任何mouseup丟失的情況,務(wù)必告訴我啊。
【清除選擇】
ie在設(shè)置setCapture之后內(nèi)容選擇都會被禁止,但也因此不會清除在設(shè)置之前就已經(jīng)選擇的內(nèi)容,而且設(shè)置之后也能通過其他方式選擇內(nèi)容,
例如用ctrl+a來選擇內(nèi)容。
ps:onkeydown、onkeyup和onkeypress事件不會受到鼠標(biāo)捕獲影響。
而ff在mousedown時就能清除原來選擇的內(nèi)容,但拖動鼠標(biāo),ctrl+a時還是會繼續(xù)選擇內(nèi)容。
不過在取消了系統(tǒng)默認(rèn)動作之后,這樣的選擇并不會對拖放操作造成影響,這里設(shè)置主要還是為了更好的體驗(yàn)。
以前我用禁止拖放對象被選擇的方法來達(dá)到目的,即ie中設(shè)置拖放對象的onselectstart返回false,在ff中設(shè)置樣式MozUserSelect(css:-moz-user-select)為none。
但這種方法只能禁止拖放對象本身被選擇,后來找到個更好的方法清除選擇,不但不影響拖放對象的選擇效果,還能對整個文檔進(jìn)行清除:
ie:document.selection.empty()
ff:window.getSelection().removeAllRanges()
為了防止在拖放過程中選擇內(nèi)容,所以把它放到Move程序中,下面是兼容的寫法:
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
【margin】
還有一個情況,當(dāng)拖放對象設(shè)置了margin,那么拖放的時候就會錯位(給SimpleDrag的拖放對象設(shè)置margin就可以測試)。
原因是在Start程序設(shè)置_x和_y時是使用offset獲取的,而這個值是包括margin的,所以在設(shè)置left和top之前要減去這個margin。
但如果在Start程序中就去掉margin那么在Move程序中設(shè)置范圍限制時就會計算錯誤,
所以最好是在Start程序中獲取值:
this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;
在Move程序中設(shè)置值:
this.Drag.style.left = iLeft - this._marginLeft + "px";
this.Drag.style.top = iTop - this._marginTop + "px";
要注意margin要在范圍修正之后再設(shè)置,否則會錯位。
【透明背景bug】
在ie有一個透明背景bug(不知算不算bug),可以用下面的代碼測試:
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<div onmousedown="alert(1)" style="border:10px solid #C4E3FD; width:50px; height:50px;position:absolute;"></div>
</body>
</html>
點(diǎn)擊div的背景會觸發(fā)不了事件(點(diǎn)擊邊框或div里面的對象是可以觸發(fā)的)。
到底什么時候會出現(xiàn)這個bug呢,再用下面的代碼測試:
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html style="background-color:#fff;">
<style>div{height:100px; border:10px solid #000;}</style>
<body style="border:10px solid #FF0000;background-color:#ccc;">
<div style="position:relative;width:150px;">
<div style="border-color:#3F3;width:30px; height:150px;background-color:#ccc; position:absolute;left:0"></div>
<img style="width:40px; height:170px; position:absolute;left:50px" src="1.jpg" temp_src="1.jpg" />
<div onclick="alert(1)" style="border-color:#00F;position:absolute;top:50px; width:200px; left:0"></div>
</div>
</body>
</html>
測試代碼中我把背景顏色(包括body)設(shè)成灰色,首先可以看出在藍(lán)色div(測試對象)內(nèi)只要觸發(fā)點(diǎn)是在灰色上面,就能觸發(fā)事件;相反,在其他不是背景的地方,即使是邊框、圖片,也不能觸發(fā)事件。
就像是把灰色的背景的補(bǔ)到藍(lán)色div上來,而且僅僅是背景能這樣,多奇怪的設(shè)定啊。
這里要說明的是body比較特別,不管背景是不是透明,只要觸發(fā)點(diǎn)是直接在body上就能觸發(fā)事件。
我的結(jié)論是如果觸發(fā)事件的對象背景是透明的,而事件的觸發(fā)點(diǎn)不在對象內(nèi)的元素上,也不是直接在body上,而且透明背景外沒有非透明背景的話,那么事件觸發(fā)就會失敗。
這個結(jié)論寫得不太好,因?yàn)槲叶疾恢脑趺幢磉_(dá)這奇怪的設(shè)定,希望各位能明白。
ps:這里設(shè)置圖片背景跟顏色背景效果是一樣的。
那最好解決的方法就是給對象設(shè)一個非透明背景,但有時需求正好是要透明的,那怎么辦呢?
首先想到的是加上背景色后設(shè)置完全透明,但這樣連邊框,容器內(nèi)的對象等都完全透明了,不夠好。
如果能保證觸發(fā)點(diǎn)直接在body或非背景上也可以,如果這個也不能保證呢?
我想到的一個解決方法是在容器里面加一個層,覆蓋整個容器,并設(shè)置背景色和完全透明:
Code
with(this._Handle.appendChild(document.createElement("div")).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";
}
到這里又不得不說ie6的一個渲染bug,用下面的代碼測試(ie6):
Code
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<body>
<div id="aa" style="width:300px;height:100px;border:1px solid;"><div style="background:#00f;height:100%;"></div></div>
<script>setTimeout("aa.style.height=200",0)</script>
</body>
</html>
可以對比ie7的效果,可以看出里面的div雖然高度設(shè)置了100%,但在外面的div修改高度之后,卻不知什么原因沒有填充了。
如果這個div是拖動對象,突然有一半拖不動了,那肯定不行。
還好BlueDestiny告訴我一個解決方法,設(shè)置對象的overflow為hidden,里面的div又會自動填充了。
BlueDestiny說“出現(xiàn)這個問題的原因是因?yàn)镮E6渲染的問題,通過某些CSS的屬性可以讓DOM改變之后再次渲染?!?
這個bug也有用zoom解決的,反正就是要使dom再次渲染。
當(dāng)發(fā)現(xiàn)程序有這個bug出現(xiàn),把程序可選參數(shù)Transparent設(shè)為true就會自動插入這樣一個層了。
到這里插入一個小知識吧,細(xì)心的話會發(fā)現(xiàn)上面的測試代碼中我給html設(shè)了一個背景色。
大家可以去掉這個背景色,會發(fā)現(xiàn)背景色會設(shè)置到整個頁面,雖然一直都是用body設(shè)置頁面背景色,但現(xiàn)在會不會有一個疑惑,body是紅色框的部分,為什么設(shè)置它的背景色就能應(yīng)用到整個頁面,而給html設(shè)置了背景色就又“正?!憋@示了呢。
這個可以從CSS21的w3c標(biāo)準(zhǔn)中關(guān)于background的部分看到原因:
For HTML documents, however, we recommend that authors specify the background for the BODY element rather than the HTML element. For HTML documents whose root HTML element has computed values of 'transparent' for 'background-color' and 'none' for 'background-image', user agents must instead use the computed value of those properties from that HTML element's first BODY element child when painting backgrounds for the canvas, and must not paint a background for that BODY element. Such backgrounds must also be anchored at the same point as they would be if they were painted only for the root element. This does not apply to XHTML documents.
我英文很爛,就勉強(qiáng)翻譯一下吧:
對于HTML文檔,我們建議作者(這是對瀏覽器的制作者說的)使用BODY元素的background,而不是HTML元素的。如果HTML文檔的根元素(HTML元素)的'background-color'是'transparent',同時'background-image'是'none'(這兩個剛好就是默認(rèn)值),那么在設(shè)置背景時就取HTML元素的第一個BODY子元素的屬性值,并且不再渲染那個元素的background。
后面兩個句就看不太懂了,不過已經(jīng)足夠解析原因了。
【iframe】
如果頁面上有嵌入iframe,那就要注意了,因?yàn)槭髽?biāo)捕獲在iframe上會有問題。
例如在拖放容器內(nèi)的一個iframe上快速移動拖放,或者鼠標(biāo)拖動到容器外的一個iframe上,反正就是鼠標(biāo)在iframe上(注意沒有其他元素隔開),就會出問題:
首先是捕獲的失效,鼠標(biāo)在iframe上,就拖不動了,但并不會觸發(fā)losecapture,更不用說window的blur了,這個在ie和ff都是一樣;
其次ie里在iframe多摩擦幾次,還可能導(dǎo)致ie死掉(原因不明)。
下面是我想到的幾種方法:
隱藏頁面的iframe,比較簡單,但可能有些在iframe中重要的信息也被隱藏,或者造成頁面布局的錯位,用戶體驗(yàn)不好;
鼠標(biāo)移動到iframe后取消拖放,難度不大,但同樣用戶體驗(yàn)不好;
每個iframe用一個透明的層遮住,很麻煩,要計算好每個iframe的位置和大??;
用一個透明的層把整個頁面遮住,比較推薦,也比較簡單,下面介紹這種做法。
我在仿LightBox內(nèi)容顯示效果做的那個覆蓋層正好能應(yīng)用在這里,首先實(shí)例化一個透明的覆蓋層:
var ol = new OverLay({ Opacity: 0 });
然后在onStart和onStop事件中添加ol.Show()和ol.Close()來顯示和隱藏覆蓋層就可以了,這樣只要不是在iframe觸發(fā)拖放就沒有問題了。
有其他更好的方法也請各位指教。
暫時就研究到這里,想不到小小的拖放就有這么多的學(xué)問。
還有滾屏等這些都還沒考慮到呢,等以后有需要了再來研究拉。

使用說明
實(shí)例化時只需要一個參數(shù),就是拖放對象:
new SimpleDrag("idDrag");
有以下這些可選參數(shù)和屬性:
屬性:默認(rèn)值//說明
Handle:"",//設(shè)置觸發(fā)對象(不設(shè)置則使用拖放對象)
Limit:false,//是否設(shè)置范圍限制(為true時下面參數(shù)有用,可以是負(fù)數(shù))
mxLeft:0,//左邊限制
mxRight:9999,//右邊限制
mxTop:0,//上邊限制
mxBottom:9999,//下邊限制
mxContainer:"",//指定限制在容器內(nèi)
LockX:false,//是否鎖定水平方向拖放
LockY:false,//是否鎖定垂直方向拖放
Lock:false,//是否鎖定
Transparent: false,//是否透明
onStart:function(){},//開始移動時執(zhí)行
onMove:function(){},//移動時執(zhí)行
onStop:function(){}//結(jié)束移動時執(zhí)行
還有屬性Drag是拖放對象,Transparent、Handle和mxContainer初始化后就不能再設(shè)置。

程序代碼
Code 
復(fù)制代碼 代碼如下:

var isIE = (document.all) ? true : false;
var $ = function (id) {
return "string" == typeof id ? document.getElementById(id) : id;
};
var Class = {
create: function() {
return function() { this.initialize.apply(this, arguments); }
}
}
var Extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
}
var Bind = function(object, fun) {
return function() {
return fun.apply(object, arguments);
}
}
var BindAsEventListener = function(object, fun) {
return function(event) {
return fun.call(object, (event || window.event));
}
}
var CurrentStyle = function(element){
return element.currentStyle || document.defaultView.getComputedStyle(element, null);
}
function addEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.addEventListener) {
oTarget.addEventListener(sEventType, fnHandler, false);
} else if (oTarget.attachEvent) {
oTarget.attachEvent("on" + sEventType, fnHandler);
} else {
oTarget["on" + sEventType] = fnHandler;
}
};
function removeEventHandler(oTarget, sEventType, fnHandler) {
if (oTarget.removeEventListener) {
oTarget.removeEventListener(sEventType, fnHandler, false);
} else if (oTarget.detachEvent) {
oTarget.detachEvent("on" + sEventType, fnHandler);
} else {
oTarget["on" + sEventType] = null;
}
};
//拖放程序
var Drag = Class.create();
Drag.prototype = {
//拖放對象
initialize: function(drag, options) {
this.Drag = $(drag);//拖放對象
this._x = this._y = 0;//記錄鼠標(biāo)相對拖放對象的位置
this._marginLeft = this._marginTop = 0;//記錄margin
//事件對象(用于綁定移除事件)
this._fM = BindAsEventListener(this, this.Move);
this._fS = Bind(this, this.Stop);
this.SetOptions(options);
this.Limit = !!this.options.Limit;
this.mxLeft = parseInt(this.options.mxLeft);
this.mxRight = parseInt(this.options.mxRight);
this.mxTop = parseInt(this.options.mxTop);
this.mxBottom = parseInt(this.options.mxBottom);
this.LockX = !!this.options.LockX;
this.LockY = !!this.options.LockY;
this.Lock = !!this.options.Lock;
this.onStart = this.options.onStart;
this.onMove = this.options.onMove;
this.onStop = this.options.onStop;
this._Handle = $(this.options.Handle) || this.Drag;
this._mxContainer = $(this.options.mxContainer) || null;
this.Drag.style.position = "absolute";
//透明
if(isIE && !!this.options.Transparent){
//填充拖放對象
with(this._Handle.appendChild(document.createElement("div")).style){
width = height = "100%"; backgroundColor = "#fff"; filter = "alpha(opacity:0)";
}
}
//修正范圍
this.Repair();
addEventHandler(this._Handle, "mousedown", BindAsEventListener(this, this.Start));
},
//設(shè)置默認(rèn)屬性
SetOptions: function(options) {
this.options = {//默認(rèn)值
Handle: "",//設(shè)置觸發(fā)對象(不設(shè)置則使用拖放對象)
Limit: false,//是否設(shè)置范圍限制(為true時下面參數(shù)有用,可以是負(fù)數(shù))
mxLeft: 0,//左邊限制
mxRight: 9999,//右邊限制
mxTop: 0,//上邊限制
mxBottom: 9999,//下邊限制
mxContainer: "",//指定限制在容器內(nèi)
LockX: false,//是否鎖定水平方向拖放
LockY: false,//是否鎖定垂直方向拖放
Lock: false,//是否鎖定
Transparent: false,//是否透明
onStart: function(){},//開始移動時執(zhí)行
onMove: function(){},//移動時執(zhí)行
onStop: function(){}//結(jié)束移動時執(zhí)行
};
Extend(this.options, options || {});
},
//準(zhǔn)備拖動
Start: function(oEvent) {
if(this.Lock){ return; }
this.Repair();
//記錄鼠標(biāo)相對拖放對象的位置
this._x = oEvent.clientX - this.Drag.offsetLeft;
this._y = oEvent.clientY - this.Drag.offsetTop;
//記錄margin
this._marginLeft = parseInt(CurrentStyle(this.Drag).marginLeft) || 0;
this._marginTop = parseInt(CurrentStyle(this.Drag).marginTop) || 0;
//mousemove時移動 mouseup時停止
addEventHandler(document, "mousemove", this._fM);
addEventHandler(document, "mouseup", this._fS);
if(isIE){
//焦點(diǎn)丟失
addEventHandler(this._Handle, "losecapture", this._fS);
//設(shè)置鼠標(biāo)捕獲
this._Handle.setCapture();
}else{
//焦點(diǎn)丟失
addEventHandler(window, "blur", this._fS);
//阻止默認(rèn)動作
oEvent.preventDefault();
};
//附加程序
this.onStart();
},
//修正范圍
Repair: function() {
if(this.Limit){
//修正錯誤范圍參數(shù)
this.mxRight = Math.max(this.mxRight, this.mxLeft + this.Drag.offsetWidth);
this.mxBottom = Math.max(this.mxBottom, this.mxTop + this.Drag.offsetHeight);
//如果有容器必須設(shè)置position為relative來相對定位,并在獲取offset之前設(shè)置
!this._mxContainer || CurrentStyle(this._mxContainer).position == "relative" || (this._mxContainer.style.position = "relative");
}
},
//拖動
Move: function(oEvent) {
//判斷是否鎖定
if(this.Lock){ this.Stop(); return; };
//清除選擇
window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
//設(shè)置移動參數(shù)
var iLeft = oEvent.clientX - this._x, iTop = oEvent.clientY - this._y;
//設(shè)置范圍限制
if(this.Limit){
//設(shè)置范圍參數(shù)
var mxLeft = this.mxLeft, mxRight = this.mxRight, mxTop = this.mxTop, mxBottom = this.mxBottom;
//如果設(shè)置了容器,再修正范圍參數(shù)
if(!!this._mxContainer){
mxLeft = Math.max(mxLeft, 0);
mxTop = Math.max(mxTop, 0);
mxRight = Math.min(mxRight, this._mxContainer.clientWidth);
mxBottom = Math.min(mxBottom, this._mxContainer.clientHeight);
};
//修正移動參數(shù)
iLeft = Math.max(Math.min(iLeft, mxRight - this.Drag.offsetWidth), mxLeft);
iTop = Math.max(Math.min(iTop, mxBottom - this.Drag.offsetHeight), mxTop);
}
//設(shè)置位置,并修正margin
if(!this.LockX){ this.Drag.style.left = iLeft - this._marginLeft + "px"; }
if(!this.LockY){ this.Drag.style.top = iTop - this._marginTop + "px"; }
//附加程序
this.onMove();
},
//停止拖動
Stop: function() {
//移除事件
removeEventHandler(document, "mousemove", this._fM);
removeEventHandler(document, "mouseup", this._fS);
if(isIE){
removeEventHandler(this._Handle, "losecapture", this._fS);
this._Handle.releaseCapture();
}else{
removeEventHandler(window, "blur", this._fS);
};
//附加程序
this.onStop();
}
};

測試代碼:
Code
復(fù)制代碼 代碼如下:

<style>
#idContainer{ border:10px solid #990000; width:600px; height:300px;}
#idDrag{ border:5px solid #C4E3FD; background:#C4E3FD; width:50px; height:50px; top:50px; left:50px;}
#idHandle{cursor:move; height:25px; background:#0000FF; overflow:hidden;}
</style>
<div id="idContainer">
<div id="idDrag"><div id="idHandle"></div></div>
</div>
<input id="idReset" type="button" value="復(fù)位" />
<input id="idLock" type="button" value="鎖定" />
<input id="idLockX" type="button" value="鎖定水平" />
<input id="idLockY" type="button" value="鎖定垂直" />
<input id="idLimit" type="button" value="范圍鎖定" />
<input id="idLimitOff" type="button" value="取消范圍鎖定" />
<br />拖放狀態(tài):<span id="idShow">未開始</span>
<script>
var drag = new Drag("idDrag", { mxContainer: "idContainer", Handle: "idHandle", Limit: true,
onStart: function(){ $("idShow").innerHTML = "開始拖放"; },
onMove: function(){ $("idShow").innerHTML = "left:"+this.Drag.offsetLeft+";top:"+this.Drag.offsetTop; },
onStop: function(){ $("idShow").innerHTML = "結(jié)束拖放"; }
});
$("idReset").onclick = function(){
drag.Limit = true;
drag.mxLeft = drag.mxTop = 0;
drag.mxRight = drag.mxBottom = 9999;
drag.LockX = drag.LockY = drag.Lock = false;
}
$("idLock").onclick = function(){ drag.Lock = true; }
$("idLockX").onclick = function(){ drag.LockX = true; }
$("idLockY").onclick = function(){ drag.LockY = true; }
$("idLimit").onclick = function(){ drag.mxRight = drag.mxBottom = 200;drag.Limit = true; }
$("idLimitOff").onclick = function(){ drag.Limit = false; }
</script>


[Ctrl+A 全選 注:引入外部Js需再刷新一下頁面才能執(zhí)行]

相關(guān)文章

最新評論