javascript html js仿LightBox內容顯示效果,div覆蓋層,鎖定頁面
JS 2009-06-15 12:06 閱讀142 評論0 字型大小: 大
大 中
中 小
小 LightBox的效果(也有的叫Windows關機效果),不過不用那麼複雜,能顯示一個內容框就行了。就是一個覆蓋全屏的層,加一個內容顯示的層。用了position:fixed這個新特性
ps:“定位效果”的意思是螢幕滾動也能固定位置。
程式說明:
要實現一個簡單的LightBox效果,主要有兩個部分:覆蓋層和高亮層。
【跨瀏覽器的固定定位】
首先要先說說這個東西position:fixed,它的作用是跨瀏覽器的固定定位。
摘自詳解定位與定位應用:
“如讓一個元素可能隨著網頁的滾動而不斷改變自己在瀏覽器的位置。而現在我可以通過CSS中的一個定位屬性來實現這樣的一個效果,這個元素屬性就是曾經不被支援的position:fixed; 他的含義就是:固定定位。這個固定與絕對位置很像,唯一不同的是絕對位置是被固定在網頁中的某一個位置,而固定定位則是固定在瀏覽器的視框位置。”
程式中很多地方利用了這個css,ie7、ff都支援這個css,但ie6不支援,程式中只能是在ie6類比這個效果。
【覆蓋層】
覆蓋層的作用是把焦點限制在高亮層上,原理是通過一個絕對位置的層(通常使用div),設定它的寬度和高度以致能覆蓋整個螢幕(包括縮放和滾動瀏覽器的情況下),再給它設定一個比較高的zIndex來層疊在原有內容之上(但要比高亮層小),這樣使用者就只能點到這個層之上的內容了。
如果初始化時沒有提供覆蓋層對象,程式中會自動建立:
this.Lay = $(this.options.Lay) || document.body.insertBefore(document.createElement("div"), document.body.childNodes[0]);
其中由於document.body.appendChild()導致IE已終止操作bug,所以用了insertBefore。。
【覆蓋螢幕】
覆蓋層的關鍵就是如何做到覆蓋整個螢幕(鎖定整個頁面),支援position:fixed的話很簡單:
with(this.Lay.style){ display = "none"; zIndex = this.zIndex; left = top = 0; position = "fixed"; width = height = "100%"; }
這樣能把瀏覽器的視框覆蓋了,其中使用了fixed樣式,這裡的意思是定位在瀏覽器的視框,並100%覆蓋。
注意不要理解錯為這個層覆蓋了整個頁面,它只是把瀏覽器可視的部分覆蓋了來達到效果。
ie6不支援怎麼辦?有幾個方法:
1,做一個覆蓋視框的層,並在onscroll時相應移動,在onresize時重新設大小;
2,做一個覆蓋視框的層,在樣式上類比fixed效果;
3,做一個層覆蓋了整個頁面的層,並在onresize時重新設大小;
方法1的缺點是滾動時很容易露出馬腳,而且不好看;方法2的缺點是需要頁面結構的改動和body樣式的修改,絕對不是好的架構;而我用的是方法3,有更好的方法歡迎提出。
用這個方法只要把position設為absolute,並使用一個_resize方法來設定width和height即可:
this.Lay.style.position = "absolute";this._resize = Bind(this, function(){ this.Lay.style.width = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth) + "px"; this.Lay.style.height = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight) + "px";});
要注意的是scrollHeight和clientHeight的區別(用Height容易測試),順帶還有offsetHeight,手冊上的說明:
scrollHeight:Retrieves the scrolling height of the object.
clientHeight:Retrieves the height of the object including padding, but not including margin, border, or scroll bar.
offsetHeight:Retrieves the height of the object relative to the layout or coordinate parent, as specified by the offsetParent property.
我的理解是:
scrollHeight是對象的內容的高度;
clientHeight是對象的可視部分高度;
offsetHeight是clientHeight加上border和捲軸本身高度。
舉個例子吧,先說說clientHeight和offsetHeight的區別(在ie7中測試):
測的是外面的div,offsetHeight和clientHeight相差17(分別是83和100),這個相差的就是那個捲軸本身的高度。
再看看clientHeight和scrollHeight的區別(下面是類比在ie中的情況):
可以看到clientHeight不受內容影響,都是83,即內容有沒有超過對象高度都不受影響,但scrollHeight會受內容高度影響,而且從測試可以意識到:
當有捲軸時,覆蓋層的高度應該取scrollHeight(內容高度);當沒有捲軸時,覆蓋層的高度應該取clientHeight(視框高度)。
而恰好兩個情況都是取兩者中比較大的值,所以就有了以下程式:
Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight) + "px";
設寬度時是不包括捲軸部分的而documentElement一般也沒有border,所以不需要offsetWidth。
上面可以看到我用的是documentElement而不是body,手冊上是這樣說的:
Retrieves a reference to the root node of the document.
意思是整個文檔的根節點,其實就是html節點(body的上一級),注意這是在XHTML的標準下。上面可以看到我們取值的對象是整個文檔而不只是body,所以這裡用documentElement。
要注意的是在window的onresize事件中scrollWidth和clientWidth的值會產生變化,程式中在onresize中使用_resize方法重新設定寬度高度:
if(isIE6){ this._resize(); window.attachEvent("onresize", this._resize); }
【覆蓋select】
自訂的層給select遮擋住是一個老問題了,不過可喜的是ie7和ff都已經支援select的zIndex,只要給層設定高的zIndex就能覆蓋select了,可惜對於ie6這個問題還是需要解決。
覆蓋select據我所知有兩個比較好的方法:
1,顯示層時,先隱藏select,關閉層時再重新顯示;
2,用一個iframe作為層的底,來遮住select。
方法1應該都明白,方法2就是利用iframe可以覆蓋select的特性,只要把一個iframe作為層的底部就可以覆蓋下面的select了,程式中是這樣使用的:
this.Lay.innerHTML = '<iframe style="position:absolute;top:0;left:0;width:100%;height:100%;filter:alpha(opacity=0);"></iframe>'
可以看出這個透明的iframe也以同樣覆蓋整個頁面,如果是有內容顯示的頁面最好設定z-index:-1;確保iframe在層的底部。
個人覺得使用方法2比較好,但始終是改變了頁面結構,有時會比較難控制,至於方法1就比較容易方便。
【高亮層】
高亮層就是用來顯示內容的層,沒什麼看頭,所以特意加了些效果在上面,吸引一下眼球。
有興趣的話可以結合拖放效果和漸層效果,做出更好的效果。
【固定定位】
這裡“固定定位”的意思是當滾動捲軸時,高亮層依然保持在瀏覽器對應的位置上,把Fixed設為true就會開啟這個效果。
同樣對於支援fixed的瀏覽器很簡單,只要把position設為fixed就行了,這個樣式本來就是這樣使用的,但可憐的ie6隻能類比了。
ie6類比的原理是在onscroll事件中,不斷根據滾動的距離修正top和left。
首先設定position為absolute,要注意的是position要在覆蓋層顯示之前顯示,否則計算覆蓋層寬高時會計算偏差(例如把頁面撐大)。
再給onscroll事件添加定位函數_fixed來修正滾屏參數:
this.Fixed && window.attachEvent("onscroll", this._fixed);
定位函數_fixed是這樣的:
this._fixed = Bind(this, function(){ this.Center ? this.SetCenter() : this.SetFixed(); });
可以看出在_fixed中,當設定了置中顯示時會執行SetCenter程式(後面會說明),否則就執行SetFixed程式。
先說說SetFixed程式的原理,就是把當前scrollTop減去_top值(上一個scrollTop值)再加上當前的offsetTop,就得到要設定的top值了:
this.Box.style.top = document.documentElement.scrollTop - this._top + this.Box.offsetTop + "px";this.Box.style.left = document.documentElement.scrollLeft - this._left + this.Box.offsetLeft + "px"; this._top = document.documentElement.scrollTop; this._left = document.documentElement.scrollLeft;
【置中顯示】
“置中顯示”的意思是高亮層位於視框左右上下置中的位置。
實現這個有兩個方法:
1,視框寬度減去高亮層寬度的一半就是置中需要的left值;
2,先設定left值為50%,然後marginLeft設為負的高亮層寬度的一半。
方法1相對方法2需要多一個視框寬度,而且方法2在縮放瀏覽器時也能保持置中,明顯方法2是更好,不過用margin會影響到left和top的計算,必須注意(例如SetFix修正的地方)。這裡我選擇了方法2,還要注意offsetWidth和offsetHeight需要在高亮層顯示之後才能擷取,所以定位程式需要放到高亮層顯示之後:
this.Box.style.top = this.Box.style.left = "50%";if(this.Fixed){ this.Box.style.marginTop = - this.Box.offsetHeight / 2 + "px"; this.Box.style.marginLeft = - this.Box.offsetWidth / 2 + "px";}else{ this.SetCenter();}
其中如果不是固定定位,需要用SetCenter程式來修正滾屏參數,SetCenter程式是這樣的:
this.Box.style.marginTop = document.documentElement.scrollTop - this.Box.offsetHeight / 2 + "px";this.Box.style.marginLeft = document.documentElement.scrollLeft - this.Box.offsetWidth / 2 + "px";
【比較文檔位置】
在ie6當不顯示覆蓋層時需要另外隱藏select,這裡使用了“覆蓋select”的方法1,值得留意的是這裡加了個select是否在高亮層的判斷:
this._select.length = 0;Each(document.getElementsByTagName("select"), Bind(this, function(o){ if(!Contains(this.Box, o)){ o.style.visibility = "hidden"; this._select.push(o); }}))
其中Contains程式是這樣的:
var Contains = function(a, b){ return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);}
作用是返回a裡面是否包含b,裡面用到了兩個函數,分別是ie的contains和ff(dom)的compareDocumentPosition。
其中contains手冊裡是這樣寫的:
Checks whether the given element is contained within the object.
意思是檢測所給對象是否包含在指定對象裡面。注意如果所給對象就是指定對象本身也會返回true,雖然這樣不太合理。
而ff的compareDocumentPosition功能更強大。
參考Comparing Document Position看下錶:
從NodeA.compareDocumentPosition(NodeB)返回的結果:
Bits Number Meaning
000000 0 Elements are identical.
000001 1 The nodes are in different documents (or one is outside of a document).
000010 2 Node B precedes Node A.
000100 4 Node A precedes Node B.
001000 8 Node B contains Node A.
010000 16 Node A contains Node B.
100000 32 For private use by the browser.
從這裡可以看出NodeA.compareDocumentPosition(NodeB) & 16的意思是當第5位元是“1”時才返回16,也就是只有NodeA包含NodeB時返回16(&是位與運算)。
ps:為什麼不直接a.compareDocumentPosition(b) == 16,我也不清楚。
<!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><meta http-equiv="Content-Type" content="text/html; charset=gb2312" /><title>JavaScript 仿LightBox內容顯示效果</title></head><body><script>var isIE = (document.all) ? true : false;var isIE6 = isIE && ([/MSIE (/d)/.0/i.exec(navigator.userAgent)][0][1] == 6);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 Each = function(list, fun){ for (var i = 0, len = list.length; i < len; i++) { fun(list[i], i); }};var Contains = function(a, b){ return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);}var OverLay = Class.create();OverLay.prototype = { initialize: function(options) { this.SetOptions(options); this.Lay = $(this.options.Lay) || document.body.insertBefore(document.createElement("div"), document.body.childNodes[0]); this.Color = this.options.Color; this.Opacity = parseInt(this.options.Opacity); this.zIndex = parseInt(this.options.zIndex); with(this.Lay.style){ display = "none"; zIndex = this.zIndex; left = top = 0; position = "fixed"; width = height = "100%"; } if(isIE6){ this.Lay.style.position = "absolute"; //ie6設定覆蓋層大小程式 this._resize = Bind(this, function(){ this.Lay.style.width = Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth) + "px"; this.Lay.style.height = Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight) + "px"; }); //遮蓋select this.Lay.innerHTML = '<iframe style="position:absolute;top:0;left:0;width:100%;height:100%;filter:alpha(opacity=0);"></iframe>' } }, //設定預設屬性 SetOptions: function(options) { this.options = {//預設值 Lay: null,//覆蓋層對象 Color: "#fff",//背景色 Opacity: 50,//透明度(0-100) zIndex: 1000//層疊順序 }; Extend(this.options, options || {}); }, //顯示 Show: function() { //相容ie6 if(isIE6){ this._resize(); window.attachEvent("onresize", this._resize); } //設定樣式 with(this.Lay.style){ //設定透明度 isIE ? filter = "alpha(opacity:" + this.Opacity + ")" : opacity = this.Opacity / 100; backgroundColor = this.Color; display = "block"; } }, //關閉 Close: function() { this.Lay.style.display = "none"; if(isIE6){ window.detachEvent("onresize", this._resize); } }};var LightBox = Class.create();LightBox.prototype = { initialize: function(box, options) { this.Box = $(box);//顯示層 this.OverLay = new OverLay(options);//覆蓋層 this.SetOptions(options); this.Fixed = !!this.options.Fixed; this.Over = !!this.options.Over; this.Center = !!this.options.Center; this.onShow = this.options.onShow; this.Box.style.zIndex = this.OverLay.zIndex + 1; this.Box.style.display = "none"; //相容ie6用的屬性 if(isIE6){ this._top = this._left = 0; this._select = []; this._fixed = Bind(this, function(){ this.Center ? this.SetCenter() : this.SetFixed(); }); } }, //設定預設屬性 SetOptions: function(options) { this.options = {//預設值 Over: true,//是否顯示覆蓋層 Fixed: false,//是否固定定位 Center: false,//是否置中 onShow: function(){}//顯示時執行 }; Extend(this.options, options || {}); }, //相容ie6的固定定位程式 SetFixed: function(){ this.Box.style.top = document.documentElement.scrollTop - this._top + this.Box.offsetTop + "px"; this.Box.style.left = document.documentElement.scrollLeft - this._left + this.Box.offsetLeft + "px"; this._top = document.documentElement.scrollTop; this._left = document.documentElement.scrollLeft; }, //相容ie6的置中定位程式 SetCenter: function(){ this.Box.style.marginTop = document.documentElement.scrollTop - this.Box.offsetHeight / 2 + "px"; this.Box.style.marginLeft = document.documentElement.scrollLeft - this.Box.offsetWidth / 2 + "px"; }, //顯示 Show: function(options) { //固定定位 this.Box.style.position = this.Fixed && !isIE6 ? "fixed" : "absolute"; //覆蓋層 this.Over && this.OverLay.Show(); this.Box.style.display = "block"; //置中 if(this.Center){ this.Box.style.top = this.Box.style.left = "50%"; //設定margin if(this.Fixed){ this.Box.style.marginTop = - this.Box.offsetHeight / 2 + "px"; this.Box.style.marginLeft = - this.Box.offsetWidth / 2 + "px"; }else{ this.SetCenter(); } } //相容ie6 if(isIE6){ if(!this.Over){ //沒有覆蓋層ie6需要把不在Box上的select隱藏 this._select.length = 0; Each(document.getElementsByTagName("select"), Bind(this, function(o){ if(!Contains(this.Box, o)){ o.style.visibility = "hidden"; this._select.push(o); } })) } //設定顯示位置 this.Center ? this.SetCenter() : this.Fixed && this.SetFixed(); //設定定位 this.Fixed && window.attachEvent("onscroll", this._fixed); } this.onShow(); }, //關閉 Close: function() { this.Box.style.display = "none"; this.OverLay.Close(); if(isIE6){ window.detachEvent("onscroll", this._fixed); Each(this._select, function(o){ o.style.visibility = "visible"; }); } }};</script><style>.lightbox{width:300px;background:#FFFFFF;border:1px solid #ccc;line-height:25px; top:20%; left:20%;}.lightbox dt{background:#f4f4f4; padding:5px;}</style><dl id="idBox" class="lightbox"> <dt id="idBoxHead"><b>LightBox</b> </dt> <dd> 內容顯示 <br /><br /> <input name="" type="button" value=" 關閉 " id="idBoxCancel" /> <br /><br /> </dd></dl><div style="margin:0 auto; width:800px; height:500px; border:1px solid #000000;"><input type="button" value="關閉覆蓋層" id="btnOverlay" /><input type="button" value="黑色覆蓋層" id="btnOverColor" /><input type="button" value="全透覆蓋層" id="btnOverOpacity" /><input type="button" value="定位效果" id="btnFixed" /><input type="button" value="置中效果" id="btnCenter" /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><br /><select><option>覆蓋select測試</option></select> <input name="" type="button" value=" 開啟 " id="idBoxOpen" /></div><script>var box = new LightBox("idBox");$("idBoxCancel").onclick = function(){ box.Close(); }$("idBoxOpen").onclick = function(){ box.Show(); }$("btnOverlay").onclick = function(){ box.Close(); if(box.Over){ box.Over = false; this.value = "開啟覆蓋層"; } else { box.Over = true; this.value = "關閉覆蓋層"; }}$("btnOverColor").onclick = function(){ box.Close(); box.Over = true; if(box.OverLay.Color == "#fff"){ box.OverLay.Color = "#000"; this.value = "白色覆蓋層"; } else { box.OverLay.Color = "#fff" this.value = "黑色覆蓋層"; }}$("btnOverOpacity").onclick = function(){ box.Close(); box.Over = true; if(box.OverLay.Opacity == 0){ box.OverLay.Opacity = 50; this.value = "全透覆蓋層"; } else { box.OverLay.Opacity = 0; this.value = "半透覆蓋層"; }}$("btnFixed").onclick = function(){ box.Close(); if(box.Fixed){ box.Fixed = false; this.value = "定位效果"; } else { box.Fixed = true; this.value = "取消定位"; }}$("btnCenter").onclick = function(){ box.Close(); if(box.Center){ box.Center = false; box.Box.style.left = box.Box.style.top = "20%"; box.Box.style.marginTop = box.Box.style.marginLeft = "0"; this.value = "置中效果"; } else { box.Center = true; this.value = "重新置放"; }}</script></body></html>
http://blog.163.com/hanguokai1987/blog/static/2702258620095150646411/