文章目錄
本文轉自:http://www.cnblogs.com/zhangziqiu/archive/2009/02/24/javascriptLibrary-4.html一.摘要
本篇文章並沒有為系列文中構造的輕量級指令碼庫添加新的方法, 而是改進了原有彈出浮動層的方法. 對方法中擷取位置的函數重構出來, 為彈出層自動添加iframe遮蓋層以實現IE6下遮住<Select>控制項. 又放在此系列文章中也是對自己學習過程的一次記錄.
二.關於彈出層無法遮蓋select的問題
在IE6下存在一個Bug: 如果彈出層是一個div, 並且在彈出層下方有一個<select>下拉框控制項, 則div無論z-index值如何設定都無法遮蓋select控制項.:
目前有兩種解決辦法:
1.當彈出層出現時隱藏select控制項.
個人認為此方法的效果欠佳, 並且沒有通用性.
2.在彈出層下添加一個一個iframe
因為在IE6中認為select控制項認為是視窗級元素. 所以可以使用同樣是視窗級元素的iframe來遮蓋住Select控制項, 注意需要將iframe的zIndex值設定為大於select控制項.
關於添加iframe的方式有很多種.比如在彈出層裡面添加一個寬度為div寬+div邊框寬的iframe(為了遮蓋彈出層邊框), 或者在頁面上添加一個透明的iframe並控制firame與彈出層的同步顯示等.
三.改進版ScriptHelper實現方法
因為我們構建的是通用的輕量級指令碼庫, 所以我不希望為了使用指令碼庫還需要在頁面上添加特別的iframe元素.而且也不希望在所有的彈出層上都添加一個iframe元素, 因為會增加彈出層的代碼.於是通過改進彈出圖層showDivCommon和關閉圖層closeDivCommon這兩個方法實現了動態添加iframe和隱藏iframe.
實現思路:
1.為每個彈出層動態在Body元素上添加一個div, div中包含一個iframe元素. 當彈出層顯示時設定iframe的位置和長寬與彈出層相同, zIndex值為彈出層-1, 關閉時彈出層時也隱藏iframe.
2.iframe和iframe的容器div在第一次彈出時建立, 以後再彈出和關閉不會重新建立.
3.每一個彈出層都會有一個對應的iframe, 以滿足一個頁面同時彈出多個彈出層的需求
實現代碼:
修改後的showDivCommon和closeDivCommon方法:
// 顯示圖層,再次調用則隱藏/* 參數說明:sObj : 要彈出圖層的事件來源divId : 要顯示的圖層ID moveTop : 手工向下移動的位移量.不移動則為0(預設).moveLeft : 手工向左移動的距離.不移動則為0(預設). 用法與測試:<div><a href="#" onclick="ScriptHelperV2.showDivCommon(this,'testDiv', 20, 20)">事件來源</a></div> */scriptHelperV2.prototype.showDivCommon = function(sObj, divId, moveTop, moveLeft) { //取消冒泡事件 if (typeof (window) != 'undefined' && window != null && window.event != null) { window.event.cancelBubble = true; } else if (ScriptHelperV2.showDivCommon.caller.arguments[0] != null) { ScriptHelperV2.showDivCommon.caller.arguments[0].cancelBubble = true; } //參數檢測.如果沒有傳入參數則設定預設值 if (moveLeft == null) { moveLeft = 0; } if (moveTop == null) { moveTop = 0; } var divObj = document.getElementById(divId); //獲得彈出圖層對象 var sObjOffsetTop = 0; //事件來源的垂直距離 var sObjOffsetLeft = 0; //事件來源的水平距離 var position = this.getPosition(sObj); //擷取事件來源對象的位移量 var myClient = this.getClient(); //擷取螢幕大小 var myScroll = this.getScroll(); //擷取捲軸滾動的舉例 var sWidth = sObj.offsetWidth != null ? parseInt(sObj.offsetWidth) : 0; //事件來源對象的寬度 var sHeight = sObj.offsetHeight != null ? parseInt(sObj.offsetHeight) : 20; //事件來源對象的高度 var popDivWidth = 0; //彈出層的寬度 var popDivHeight = 0; //彈出層的高度 var bottomSpace; //距離底部的距離 var iframeDivId = "tempIframeDiv" + divId; //iframe所在div的id var iframeId = "tempIframe" + divId; //iframe的id var iframeDiv = document.getElementById(iframeDivId); //iframe所在div對象 var iframe = document.getElementById(iframeId); //iframe對象 if (divObj.style.display.toLowerCase() != "none") { //隱藏圖層 divObj.style.display = "none"; //隱藏iframe if (iframe != null) { iframe.style.display = "none"; } if (iframeDiv != null) { iframeDiv.style.display = "none"; } } else { if (sObj == null) { alert("事件來源對象為null"); return false; } //先顯示圖層,才能擷取到彈出層的長寬 divObj.style.display = "block"; popDivWidth = divObj.offsetWidth != null ? parseInt(sObj.offsetWidth) : 0; //彈出層寬度 popDivHeight = divObj.offsetHeight != null ? parseInt(divObj.offsetHeight) : 0; //彈出層高度 /* 擷取距離底部的距離 */ bottomSpace = parseInt(myClient.clientHeight) - (parseInt(position.OffsetTop) - parseInt(myScroll.scrollTop)) - parseInt(sHeight); /* 設定圖層顯示位置 */ //如果事件來源下方空間不足且上方控制項足夠容納彈出層,則在上方顯示.否則在下方顯示 if (popDivHeight > 0 && bottomSpace < popDivHeight && position.OffsetTop > popDivHeight) { divObj.style.top = (parseInt(position.OffsetTop) - parseInt(popDivHeight)).toString() + "px"; } else { divObj.style.top = (parseInt(position.OffsetTop) + parseInt(sHeight)).toString() + "px"; } divObj.style.left = (parseInt(position.OffsetLeft) - parseInt(moveLeft)).toString() + "px"; } //如果遮蓋iframe層不存在則建立 if (iframe == null) { //ie6下使用dom添加節點後無法控制某些屬性, 所以將iframe放在一個div中,這樣才可以用寫html的方式添加. var tempIframeDiv = document.createElement("div"); tempIframeDiv.setAttribute("id", iframeDivId); document.body.appendChild(tempIframeDiv); var iframeString = "<iframe id=\"" + iframeId + "\" style=\"position: absolute; display:none; border-width:0px;\"></iframe>"; tempIframeDiv.innerHTML = iframeString; iframe = document.getElementById(iframeId); iframeDiv = document.getElementById(iframeDivId); } //使用遮蓋層遮住select控制項 if (iframe != null && iframeDiv != null) { iframeDiv.style.display = "block"; iframe.style.top = divObj.style.top; iframe.style.left = divObj.style.left; iframe.style.width = divObj.offsetWidth.toString() + "px"; iframe.style.height = divObj.offsetHeight.toString() + "px"; iframe.style.display = "block"; iframe.style.zIndex = divObj.style.zIndex - 1; }}
// 關閉圖層/* 參數說明:divId : 要隱藏的圖層ID 用法與測試:ScriptHelperV2.closeDivCommon('testDiv'); */scriptHelperV2.prototype.closeDivCommon = function(divId) { var iframeDivId = "tempIframeDiv" + divId; //iframe所在div的id var iframeId = "tempIframe" + divId; //iframe的id var divObj = document.getElementById(divId); //獲得圖層對象 if (divObj != null) { divObj.style.display = "none"; } var iframe = document.getElementById(iframeId); if (iframe != null) { iframe.style.display = "none"; } var iframeDiv = document.getElementById(iframeDivId); if (iframeDiv != null) { iframe.style.display = "none"; } }
四. 其他改進
和本系列文章第一版本的方法比較, showDivCommon方法還做了如下改進:
1. 將計算座標的方法抽象出來:
//擷取對象相對於Body對象的位移量座標.需要在Body元素加上position:relative, 並且保證任何父級元素都沒有position:relative/* 參數說明:sObj : 要彈出圖層的事件來源 用法與測試: var sObj = document.getElementById("divId");var position = ScriptHelperV2.getPosition(sObj);var sObjOffsetTop = parseInt( position.OffsetTop );var sObjOffsetLeft = parseInt( position.OffsetLeft );*/scriptHelperV2.prototype.getPosition = function(sObj) { var sObjOffsetTop = 0; //事件來源的垂直距離 var sObjOffsetLeft = 0; //事件來源的水平距離 /* 擷取事件來源對象的位移量 */ var tempObj = sObj; //用於計算事件來源座標的臨時對象 while (tempObj && tempObj.tagName.toUpperCase() != "BODY") { sObjOffsetTop += tempObj.offsetTop; sObjOffsetLeft += tempObj.offsetLeft; tempObj = tempObj.offsetParent; } tempObj = null; return { OffsetTop: sObjOffsetTop, OffsetLeft: sObjOffsetLeft };}
2.由於經驗淺薄, 原以為擷取不到對象的高度和寬度, 經過學習發現可以使用OffsetWidth和OffsetHeight擷取. 所以進一步規範了showDivCommon的參數.現在只傳入兩個參數使用時的座標計算更加正確:
<a class="cursorHand" onclick="ScriptHelperV2.showDivCommon(this,'subMenu1');">Menu1</a>
五.執行個體
原ScriptHelper實現效果:
新ScriptHelperV2實現效果:注意已經遮蓋住了select控制項
執行個體代碼:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" ><head> <title>ScriptHelper 類測試頁面 - ShowDivComon 顯示彈出層方法</title> <!--<script src="http://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript"></script>--> <script src="http://files.cnblogs.com/zhangziqiu/ScriptHelperV2.js" type="text/javascript"></script> <style type="text/css"> .cursorHand { cursor:pointer;} </style> </head><body style="position:relative;"> <div style="height:300px;"></div> <!-- Main Menu --> <div > <a class="cursorHand" onclick="ScriptHelperV2.showDivCommon(this,'subMenu1');">Menu1</a> </div> <div> <select id="Select1" style="z-index:1;"> <option>123</option> <option>456</option> </select> </div> <!-- Sub Menu 1 --> <div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; width:200px;z-index:100;"> <div>SubMent-1</div> <div>SubMent-2</div> <div>SubMent-3</div> <div>SubMent-4</div> <div>SubMent-5</div> <div>SubMent-6</div> </div> </body></html>
六.打包
http://files.cnblogs.com/zhangziqiu/TestScriptHelper_ShowDivComon.rar
javascript檔案:
http://files.cnblogs.com/zhangziqiu/ScriptHelperV2.js
七.相關經驗和技巧
- 一個li對象的width設定為100px或者li的容器ul的width為100px, 在firefox下, li的offsetWidth永遠為100, 超出的內容部分不會自動撐開li, 即使設定了overflow:visible仍然只是在li外部顯示超出內容. 解決辦法是可以li內再增加一個span對象放置內容文字, 這時擷取span的offsetWidth為內容的長度而不是li的長度.
- 使用document.createElement方法建立的對象,在IE6下雖然可以擷取到對象, 但是無法再設定他的寬度和高度. 也就是說createElement在IE和FF下均可用, 但是要想動態建立一個標籤並且設定他的寬高,比如一個<iframe>, 可以使用寫入HTML代碼的方式,寫入後可以擷取對象並設定他的寬度和高度.
- 一個div,裡面有一個iframe, 關閉時一定要先關閉iframe再關閉div, 否則在ie6中iframe會無法關閉.
八.總結
這篇文章沒有太多技術含量, 高手們請見諒. 在改進中我學習到了新的知識.希望能和鳥兒們一起進步.
作者:張子秋
出處:http://www.cnblogs.com/zhangziqiu/
本文著作權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則保留追究法律責任的權利。