• JavaScript 仿LightBox内容显示效果


    近来要做一个LightBox的效果(也有的叫Windows关机效果),不过不用那么复杂,能显示一个内容框就行了。
    这个效果很久以前就做过,无非就是一个覆盖全屏的层,加一个内容显示的层。不过showbo教了我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(thisfunction(){
        
    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;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(thisfunction(){ 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(thisfunction(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)返回的结果:

    BitsNumberMeaning
    0000000Elements are identical.
    0000011The nodes are in different documents (or one is outside of a document).
    0000102Node B precedes Node A.
    0001004Node A precedes Node B.
    0010008Node B contains Node A.
    01000016Node A contains Node B.
    10000032For private use by the browser.

    从这里可以看出NodeA.compareDocumentPosition(NodeB) & 16的意思是当第5位数是“1”时才返回16,也就是只有NodeA包含NodeB时返回16(&是位与运算)。
    ps:为什么不直接a.compareDocumentPosition(b) == 16,我也不清楚。


    程序代码:


    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(thisfunction(){
                
    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;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 = 0this._select = [];
            
    this._fixed = Bind(thisfunction(){ 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(thisfunction(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"; });
        }
      }
    };



    使用说明: 

    首先要有一个高亮层:


    <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//层叠顺序


    完整实例下载

  • 相关阅读:
    日期 根据所选日期 获取 之后N天的日期
    错误退出登录
    挂载路由导航守卫 router
    缓存 ssessionStorage&localStorage
    vue项目 第三方图标库阿里图库
    码云新建仓库 以及本地上传
    sql的四种连接-左外连接、右外连接、内连接、全连接
    C#中常用修饰符
    接口的隐式和显式实现
    C#break、continue、return、goto
  • 原文地址:https://www.cnblogs.com/top5/p/1848511.html
Copyright © 2020-2023  润新知