一.摘要
本系列文章是為了抽象通用的,跨瀏覽器的指令碼方法.
本篇文章講解彈出浮動層的javascript函數, 以及函數的原理和使用注意事項.
二.實現效果
用指令碼彈出浮動層是我們最常用的指令碼方法之一.下面是:
點擊圖中的"航空公司"後,會在"航空公司"下面彈出浮動層.
在網上彈出框的指令碼相當多, 而且還有各種第三方JS架構可供我們使用.但是其中有的指令碼過於簡單,僅僅粗略的實現彈出效果而忽略了靈活性,通用性和跨瀏覽器特性. 使用JS架構又有些殺雞用牛刀.所以在收集整理了一些資料後, 寫出了下文中的ScriptHelper類的彈出層方法.
主要特點有:
支援多瀏覽器
使用物件導向方法封裝
使用簡單,通用性強.
將計算位置等函數進行提取, 所有的相關函數都可以單獨調用, 可根據具體項目繼續二次開發.
三.指令碼方法
下面我先將指令碼方法貢獻出來,然後舉例如何使用. 最後講解指令碼的原理.
複製代碼 代碼如下:/* ==================== ScriptHelper 開始 ==================== */
/* scriptHelper 指令碼協助對象.
建立人: ziqiu.zhang 2008.3.5
添加函數:
getScroll():得到滑鼠滾過的距離-相容XHTML
getClient():得到瀏覽器當前顯示地區的大小-相容XHTML
showDivCommon():顯示圖層.
使用舉例:
<div id="testDiv" style="display:none; position:absolute; border:1px #000000;">我是測試圖層我是測試圖層</div>
<div style="width:400px; text-align:center;"><div><a href="#" onclick="ScriptHelper.showDivCommon(this,'testDiv', 20, 70)">事件來源</a></div></div>
*/
function scriptHelper()
{
}
// 得到滑鼠滾過的距離 scrollTop 與 scrollLeft
/* 用法與測試:
var myScroll = getScroll();
alert("myScroll.scrollTop:" + myScroll.scrollTop);
alert("myScroll.scrollLeft:" + myScroll.scrollLeft);
*/
scriptHelper.prototype.getScroll = function ()
{
var scrollTop = 0, scrollLeft = 0;
scrollTop = (document.body.scrollTop > document.documentElement.scrollTop)? document.body.scrollTop:document.documentElement.scrollTop;
if( isNaN(scrollTop) || scrollTop <0 ){ scrollTop = 0 ;}
scrollLeft = (document.body.scrollLeft > document.documentElement.scrollLeft )? document.body.scrollLeft:document.documentElement.scrollLeft;
if( isNaN(scrollLeft) || scrollLeft <0 ){ scrollLeft = 0 ;}
return { scrollTop:scrollTop, scrollLeft: scrollLeft};
}
// 得到瀏覽器當前顯示地區的大小 clientHeight 與 clientWidth
/* 用法與測試:
var myScroll = getScroll();
alert("myScroll.sTop:" + myScroll.sTop);
alert("myScroll.sLeft:" + myScroll.sLeft);
*/
scriptHelper.prototype.getClient = function ()
{
//判斷頁面是否符合XHTML標準
var isXhtml = true;
if( document.documentElement == null || document.documentElement.clientHeight <= 0)
{
if( document.body.clientHeight>0 )
{
isXhtml = false;
}
}
this.clientHeight = isXhtml?document.documentElement.clientHeight:document.body.clientHeight;
this.clientWidth = isXhtml?document.documentElement.clientWidth:document.body.clientWidth;
return {clientHeight:this.clientHeight,clientWidth:this.clientWidth};
}
// 顯示圖層,再次調用則隱藏
/* 參數說明:
sObj : 要彈出圖層的事件來源
divId : 要顯示的圖層ID
sObjHeight : 事件來源的高度,預設為20.需要手工傳入是因為對於由於事件來源對象可能是各種HTML元素,有些元素高度的計算無法跨瀏覽器通用.
moveLeft : 手工向左移動的距離.不移動則為0(預設).
divObjHeight: 彈出層的高度.如果傳入大於0的此參數, 則當事件來源下方空間不足時,在事件來源上方彈出層.如果不傳此參數則一直在事件來源下方彈出.
用法與測試:
<div><a href="#" onclick="ScriptHelper.showDivCommon(this,'testDiv', 20, 20)">事件來源</a></div>
*/
scriptHelper.prototype.showDivCommon = function (sObj,divId, sObjHeight, moveLeft, divObjHeight)
{
//取消冒泡事件
if( typeof(window)!='undefined' && window != null && window.event != null )
{
window.event.cancelBubble = true;
}
else if( ScriptHelper.showDivCommon.caller.arguments[0] != null )
{
ScriptHelper.showDivCommon.caller.arguments[0].cancelBubble = true;
}
//參數檢測.如果沒有傳入參數則設定預設值
if( moveLeft == null )
{
moveLeft = 0;
}
if( sObjHeight == null )
{
sObjHeight = 20;
}
if(divObjHeight == null)
{
divObjHeight = 0;
}
var divObj = document.getElementById(divId); //獲得圖層對象
var sObjOffsetTop = 0; //事件來源的垂直距離
var sObjOffsetLeft = 0; //事件來源的水平距離
var myClient = this.getClient();
var myScroll = this.getScroll();
var sWidth = sObj.width; //事件來源對象的寬度
var sHeight = sObjHeight; //事件來源對象的高度
var bottomSpace; //距離底部的距離
/* 擷取事件來源控制項的高度和寬度.*/
if( sWidth == null )
{
sWidth = 100;//無法擷取則為100
}
else
{
sWidth = sWidth + 1; //留出1px的距離
}
if( divObj.style.display.toLowerCase() != "none" )
{
//隱藏圖層
divObj.style.display = "none";
}
else
{
if( sObj == null )
{
alert("事件來源對象為null");
return false;
}
/* 擷取事件來源對象的位移量 */
var tempObj = sObj; //用於計算事件來源座標的臨時對象
while( tempObj && tempObj.tagName.toUpperCase() != "BODY" )
{
sObjOffsetTop += tempObj.offsetTop;
sObjOffsetLeft += tempObj.offsetLeft;
tempObj = tempObj.offsetParent;
}
tempObj = null;
/* 擷取距離底部的距離 */
bottomSpace = parseInt(myClient.clientHeight) - ( parseInt(sObjOffsetTop) - parseInt(myScroll.scrollTop)) - parseInt(sHeight);
/* 設定圖層顯示位置 */
//如果事件來源下方空間不足且上方控制項足夠容納彈出層,則在上方顯示.否則在下方顯示
if( divObjHeight>0 && bottomSpace < divObjHeight && sObjOffsetTop >divObjHeight )
{
divObj.style.top = ( parseInt( sObjOffsetTop ) - parseInt( divObjHeight ) - 10).toString() + "px";
}
else
{
divObj.style.top = ( parseInt( sObjOffsetTop ) + parseInt( sHeight ) ).toString() + "px";
}
divObj.style.left = ( parseInt( sObjOffsetLeft ) - parseInt( moveLeft ) ).toString() + "px";
divObj.style.display="block";
}
}
// 關閉圖層
/* 參數說明:
divId : 要隱藏的圖層ID
用法與測試:
ScriptHelper.closeDivCommon('testDiv');
*/
scriptHelper.prototype.closeDivCommon = function (divId)
{
//
var divObj = document.getElementById(divId); //獲得圖層對象
if( divObj != null )
{
divObj.style.display = "none";
}
}
//建立scriptHelper類的一個執行個體對象.全域使用.
var ScriptHelper = new scriptHelper();
/* ==================== ScriptHelper 結束 ==================== */
四.使用舉例
接下來我們建立HTML頁面示範如何使用此指令碼.此執行個體是一個菜單,當點擊時顯示子功能表圖層.
1.引用指令檔
將上面的代碼儲存在ScriptHelper.js檔案中.在頁面中添加引用:
<script src="http://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript" defer="defer"></script>
2.編寫子功能表
先編寫兩個子功能表圖層. 複製代碼 代碼如下:<!-- Sub Menu 1 -->
<div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; height:100px;">
<div>1-1</div>
<div>1-2</div>
</div>
<!-- Sub Menu 2 -->
<div id="subMenu2" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; padding:5px;" >
<div>2-1</div>
<div>2-2</div>
</div>
對於子功能表, 最重要的就是要設定兩個樣式:position和display.
position:absolute 讓此圖層能夠精確定位顯示.從而控制他的顯示位置.
display:none 讓圖層在載入時不顯示.
3.編寫主菜單
主菜單代碼如下: 複製代碼 代碼如下:<!-- Main Menu -->
<div >
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu1', 20, 0, 100)">Menu1</a>
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu2', 20, 0)">Menu2</a>
<a class="cursorHand" href="#">NoSubMenu</a>
</div>
我們建立了三個菜單.其中Menu1和Menu2擁有子功能表, NoSubMenu沒有子功能表.
我們使用了a元素建立菜單對象, 但是因為沒有為其添加href屬性,所以預設情況下滑鼠放上去不會變成hand圖形.需要為其添加樣式cursorHand,次樣式的代碼如下:
<style type="text/css">
.cursorHand { cursor:pointer;}
</style>
最關鍵的是為菜單添加的onclick事件, 此事件調用ScriptHelper.showDivCommon方法用於顯示圖層.
方法第一個參數值為this表示將事件來源對象傳入, 在函數中會根據事件來源計算顯示位置.
方法第二個參數表示彈出圖的Id
方法第三個參數是選擇性參數, 用於設定向下的位移量.因為我們計算的位置是"<a>Menu1</a>"這個元素的左上方座標.需要設定一個向下的位移量.一般設定為事件來源的高度,預設為20px.
方法第四個參數是選擇性參數,用於設定向左的位移量.原因同上.預設為0px;
方法第五個參數是選擇性參數,需要傳入彈出層的高度.如果使用了此屬性則彈出層可能彈出在事件來源上方.不傳遞此屬性則始終在事件來源下方彈出圖層.
4.效果與完整代碼
完整執行個體代碼如下:
複製代碼 代碼如下:<!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 類測試頁面</title>
<script src="http://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript" defer="defer"></script>
<style type="text/css">
.cursorHand { cursor:pointer;}
</style>
</head>
<body style="position:relative;">
<div style="height:200px;"></div>
<!-- Main Menu -->
<div >
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu1', 20, 0, 100)">Menu1</a>
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu2', 20, 0)">Menu2</a>
<a class="cursorHand" href="#">NoSubMenu</a>
</div>
<!-- Sub Menu 1 -->
<div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; height:100px;">
<div>1-1</div>
<div>1-2</div>
</div>
<!-- Sub Menu 2 -->
<div id="subMenu2" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; padding:5px;" >
<div>2-1</div>
<div>2-2</div>
</div>
</body>
</html>
五.注意事項:
1.要給Body元素加上position:relative樣式:
<body style="position:relative;">
不增加的話在IE6下有時會出現無法精確定位事件來源的情況.比如在menu的<div>上添加幾個<br/>,則彈出層的位置就發生錯誤了.
如果在Body元素上增加此樣式仍然彈出位置錯誤,則請在事件來源對象的容器元素中也添加此樣式
2.不傳遞最後一個參數則彈出層只在事件來源下面彈出.否則將會計算事件來源底部的舉例, 如果底部控制項不足並且上部控制項充足,則彈出層顯示在事件來源上方.
3.在頁面上要添加DOCTYPE元素.不添加的話很可能在某些瀏覽器中出現錯誤.有關DOCTYPE的作用,請查看下面的文章:
DOCTYPE元素解析
六.總結
相容多瀏覽器真的是一件讓人頭疼的事情.我估計此函數還是會有問題.本來想寫指令碼分析的, 但是在寫作的時候又發現了一些Bug並且進行了修正.相容來相容去最後把自己相容暈了.其實如果一個項目能使用指令碼庫將會是一個很好的選擇.本系列文章只是希望構建一個輕量級的指令碼類庫.大家使用中有任何問題希望多多交流, 一起打造簡單易用的指令碼庫!