• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
激情 希望 斗志昂扬
Records of growth process 专注微软技术
博客园    首页    新随笔    联系   管理    订阅  订阅

【转】JavaScript 仿LightBox内容显示效果

近来要做一个LightBox的效果(也有的叫Windows关机效果),不过不用那么复杂,能显示一个内容框就行了。
这个效果很久以前就做过,无非就是一个覆盖全屏的层,加一个内容显示的层。不过showbo教了我position:fixed这个新特性,决定重写一遍。

先看效果:

LightBox
内容显示







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即可:

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

Code
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需要在高亮层显示之后才能获取,所以定位程序需要放到高亮层显示之后:

Code
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程序是这样的:

Code
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是否在高亮层的判断:

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


程序代码:

 

Code
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"; });
    }
  }
};



使用说明:

首先要有一个高亮层:

Code
<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>

至于覆盖层一般不需要另外设了,接着就可以实例化一个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//层叠顺序

 

完整实例下载

posted @ 2009-03-25 10:37  贤  阅读(183)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3