天天看點

JavaScript 仿LightBox内容顯示效果

近來要做一個LightBox的效果(也有的叫Windows關機效果),不過不用那麼複雜,能顯示一個内容框就行了。

這個效果很久以前就做過,無非就是一個覆寫全屏的層,加一個内容顯示的層。不過showbo教了我position:fixed這個新特性,決定重寫一遍。

先看效果:

 覆寫select測試  

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]);

【覆寫螢幕】

覆寫層的關鍵就是如何做到覆寫整個螢幕(鎖定整個頁面),支援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即可:

JavaScript 仿LightBox内容顯示效果

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值了:

JavaScript 仿LightBox内容顯示效果

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需要在高亮層顯示之後才能擷取,是以定位程式需要放到高亮層顯示之後:

JavaScript 仿LightBox内容顯示效果

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程式是這樣的:

JavaScript 仿LightBox内容顯示效果

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是否在高亮層的判斷:

JavaScript 仿LightBox内容顯示效果

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功能更強大。

從NodeA.compareDocumentPosition(NodeB)傳回的結果:

Bits

Number

Meaning

000000

Elements are identical.

000001

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,我也不清楚。

程式代碼:

JavaScript 仿LightBox内容顯示效果

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 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.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用的屬性

        this._top = this._left = 0; this._select = [];

        this._fixed = Bind(this, function(){ this.Center ? this.SetCenter() : this.SetFixed(); });

        Over:    true,//是否顯示覆寫層

        Fixed:    false,//是否固定定位

        Center:    false,//是否居中

        onShow:    function(){}//顯示時執行

  //相容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();

        }

        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();

    this.OverLay.Close();

        window.detachEvent("onscroll", this._fixed);

        Each(this._select, function(o){ o.style.visibility = "visible"; });

使用說明: 

首先要有一個高亮層:

JavaScript 仿LightBox内容顯示效果

<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" />

  </dd>

</dl>

至于覆寫層一般不需要另外設了,接着就可以執行個體化一個LightBox:

var box = new LightBox("idBox");

打開和關閉LightBox分别是Show()和Close()方法,

其中LightBox有下面幾個屬性:

屬性:預設值//說明

Over:true,//是否顯示覆寫層

Fixed:false,//是否固定定位

Center:false,//是否居中

onShow:function(){}//顯示時執行

還有OverLay屬性是覆寫層對象,它也有幾個屬性:

Lay:null,//覆寫層對象

Color:"#fff",//背景色

Opacity:50,//透明度(0-100)

zIndex:1000//層疊順序

<a href="http://files.cnblogs.com/cloudgamer/LightBox.rar">完整執行個體下載下傳</a>

繼續閱讀