• Javascript公共脚本库系列(四) 改进的弹出层脚本


    本文转自:http://www.cnblogs.com/zhangziqiu/archive/2009/02/24/javascriptLibrary-4.html

    一.摘要

    本篇文章并没有为系列文中构造的轻量级脚本库添加新的方法, 而是改进了原有弹出浮动层的方法. 对方法中获取位置的函数重构出来, 为弹出层自动添加iframe遮盖层以实现IE6下遮住<Select>控件. 又放在此系列文章中也是对自己学习过程的一次记录.

    二.关于弹出层无法遮盖select的问题

    在IE6下存在一个Bug: 如果弹出层是一个div, 并且在弹出层下方有一个<select>下拉框控件, 则div无论z-index值如何设置都无法遮盖select控件.如图:

    image

    目前有两种解决办法:

    1.当弹出层出现时隐藏select控件.

    个人认为此方法的效果欠佳, 并且没有通用性.

    2.在弹出层下添加一个一个iframe

    因为在IE6中认为select控件认为是窗口级元素. 所以可以使用同样是窗口级元素的iframe来遮盖住Select控件, 注意需要将iframe的zIndex值设置为大于select控件.

    关于添加iframe的方式有很多种.比如在弹出层里面添加一个宽度为div宽+div边框宽的iframe(为了遮盖弹出层边框), 或者在页面上添加一个透明的iframe并控制firame与弹出层的同步显示等.

    三.改进版ScriptHelper实现方法

    因为我们构建的是通用的轻量级脚本库, 所以我不希望为了使用脚本库还需要在页面上添加特别的iframe元素.而且也不希望在所有的弹出层上都添加一个iframe元素, 因为会增加弹出层的代码.于是通过改进弹出图层showDivCommon和关闭图层closeDivCommon这两个方法实现了动态添加iframe和隐藏iframe.

    实现思路:

    1.为每个弹出层动态的在Body元素上添加一个div, div中包含一个iframe元素. 当弹出层显示时设置iframe的位置和长宽与弹出层相同, zIndex值为弹出层-1, 关闭时弹出层时也隐藏iframe.

    2.iframe和iframe的容器div在第一次弹出时创建, 以后再弹出和关闭不会重新创建.

    3.每一个弹出层都会有一个对应的iframe, 以满足一个页面同时弹出多个弹出层的需求

    实现代码:

    修改后的showDivCommon和closeDivCommon方法:

    //  显示图层,再次调用则隐藏
    /*  参数说明:
    sObj        : 要弹出图层的事件源
    divId       : 要显示的图层ID    
    moveTop     : 手工向下移动的偏移量.不移动则为0(默认).
    moveLeft    : 手工向左移动的距离.不移动则为0(默认).
        
    用法与测试:
    <div><a href="#" onclick="ScriptHelperV2.showDivCommon(this,'testDiv', 20, 20)">事件源</a></div>  
    */
    scriptHelperV2.prototype.showDivCommon = function(sObj, divId, moveTop, moveLeft) {
    
        //取消冒泡事件
        if (typeof (window) != 'undefined' && window != null && window.event != null) {
            window.event.cancelBubble = true;
        }
        else if (ScriptHelperV2.showDivCommon.caller.arguments[0] != null) {
            ScriptHelperV2.showDivCommon.caller.arguments[0].cancelBubble = true;
        }
    
        //参数检测.如果没有传入参数则设置默认值
        if (moveLeft == null) {
            moveLeft = 0;
        }
        if (moveTop == null) {
            moveTop = 0;
        }
    
        var divObj = document.getElementById(divId); //获得弹出图层对象    
        var sObjOffsetTop = 0;      //事件源的垂直距离
        var sObjOffsetLeft = 0;     //事件源的水平距离
    
        var position = this.getPosition(sObj); //获取事件源对象的偏移量
        var myClient = this.getClient();       //获取屏幕大小  
        var myScroll = this.getScroll();       //获取滚动条滚动的举例
        var sWidth = sObj.offsetWidth != null ? parseInt(sObj.offsetWidth) : 0;    //事件源对象的宽度
        var sHeight = sObj.offsetHeight != null ? parseInt(sObj.offsetHeight) : 20; //事件源对象的高度
        var popDivWidth = 0;    //弹出层的宽度
        var popDivHeight = 0;   //弹出层的高度
        var bottomSpace;        //距离底部的距离
    
        var iframeDivId = "tempIframeDiv" + divId;  //iframe所在div的id
        var iframeId = "tempIframe" + divId;        //iframe的id
        var iframeDiv = document.getElementById(iframeDivId); //iframe所在div对象
        var iframe = document.getElementById(iframeId); //iframe对象
    
    
        if (divObj.style.display.toLowerCase() != "none") {
            //隐藏图层
            divObj.style.display = "none";
            //隐藏iframe
            if (iframe != null) {
                iframe.style.display = "none";
            }
    
            if (iframeDiv != null) {
                iframeDiv.style.display = "none";
            }
        }
        else {
            if (sObj == null) {
                alert("事件源对象为null");
                return false;
            }
    
            //先显示图层,才能获取到弹出层的长宽
            divObj.style.display = "block";
            popDivWidth = divObj.offsetWidth != null ? parseInt(sObj.offsetWidth) : 0;      //弹出层宽度
            popDivHeight = divObj.offsetHeight != null ? parseInt(divObj.offsetHeight) : 0;  //弹出层高度
    
            /* 获取距离底部的距离 */
            bottomSpace = parseInt(myClient.clientHeight) - (parseInt(position.OffsetTop) - parseInt(myScroll.scrollTop)) - parseInt(sHeight);
    
            /* 设置图层显示位置 */
            //如果事件源下方空间不足且上方控件足够容纳弹出层,则在上方显示.否则在下方显示
            if (popDivHeight > 0 && bottomSpace < popDivHeight && position.OffsetTop > popDivHeight) {
                divObj.style.top = (parseInt(position.OffsetTop) - parseInt(popDivHeight)).toString() + "px";
            }
            else {
                divObj.style.top = (parseInt(position.OffsetTop) + parseInt(sHeight)).toString() + "px";
    
            }
            divObj.style.left = (parseInt(position.OffsetLeft) - parseInt(moveLeft)).toString() + "px";
    
    
        }
    
        //如果遮盖iframe层不存在则创建
        if (iframe == null) {
            //ie6下使用dom添加节点后无法控制某些属性, 所以将iframe放在一个div中,这样才可以用写html的方式添加. 
            var tempIframeDiv = document.createElement("div");
            tempIframeDiv.setAttribute("id", iframeDivId);
            document.body.appendChild(tempIframeDiv);
    
            var iframeString = "<iframe id=\"" + iframeId + "\" style=\"position: absolute; display:none; border-0px;\"></iframe>";
            tempIframeDiv.innerHTML = iframeString;
            iframe = document.getElementById(iframeId);
            iframeDiv = document.getElementById(iframeDivId);
        }
        //使用遮盖层遮住select控件
        if (iframe != null && iframeDiv != null) {
            iframeDiv.style.display = "block";
            iframe.style.top = divObj.style.top;
            iframe.style.left = divObj.style.left;
            iframe.style.width = divObj.offsetWidth.toString() + "px";
            iframe.style.height = divObj.offsetHeight.toString() + "px";
            iframe.style.display = "block";
            iframe.style.zIndex = divObj.style.zIndex - 1;
        }
    
    }
    
    
    //  关闭图层
    /*  参数说明:
    divId        : 要隐藏的图层ID    
        
    用法与测试:
    ScriptHelperV2.closeDivCommon('testDiv');    
    */
    scriptHelperV2.prototype.closeDivCommon = function(divId) {
    
        var iframeDivId = "tempIframeDiv" + divId;  //iframe所在div的id
        var iframeId = "tempIframe" + divId;        //iframe的id
        
        var divObj = document.getElementById(divId); //获得图层对象    
        if (divObj != null) {
            divObj.style.display = "none";
        }
    
        var iframe = document.getElementById(iframeId);
        if (iframe != null) {
            iframe.style.display = "none";
        }
    
        var iframeDiv = document.getElementById(iframeDivId);
        if (iframeDiv != null) {
            iframe.style.display = "none";
        } 
    }

    四. 其他改进

    和本系列文章第一版本的方法比较,  showDivCommon方法还做了如下改进:

    1. 将计算坐标的方法抽象出来:

    //获取对象相对于Body对象的偏移量坐标.需要在Body元素加上position:relative, 并且保证任何父级元素都没有position:relative
    /*  参数说明:
    sObj      : 要弹出图层的事件源
        
    用法与测试: 
    var sObj = document.getElementById("divId");
    var position = ScriptHelperV2.getPosition(sObj);
    var sObjOffsetTop = parseInt(  position.OffsetTop );
    var sObjOffsetLeft = parseInt( position.OffsetLeft );
    */
    scriptHelperV2.prototype.getPosition = function(sObj) {
        var sObjOffsetTop = 0;      //事件源的垂直距离
        var sObjOffsetLeft = 0;     //事件源的水平距离
    
        /* 获取事件源对象的偏移量 */
        var tempObj = sObj; //用于计算事件源坐标的临时对象
        while (tempObj && tempObj.tagName.toUpperCase() != "BODY") {
            sObjOffsetTop += tempObj.offsetTop;
            sObjOffsetLeft += tempObj.offsetLeft;
            tempObj = tempObj.offsetParent;
        }
        tempObj = null;
        return { OffsetTop: sObjOffsetTop, OffsetLeft: sObjOffsetLeft };
    }

    2.由于经验浅薄, 原以为获取不到对象的高度和宽度,  经过学习发现可以使用OffsetWidth和OffsetHeight获取. 所以进一步规范了showDivCommon的参数.现在只传入两个参数使用时的坐标计算更加正确:

    <a class="cursorHand" onclick="ScriptHelperV2.showDivCommon(this,'subMenu1');">Menu1</a>

    五.实例

    原ScriptHelper实现效果:

    image

    新ScriptHelperV2实现效果:注意已经遮盖住了select控件

    image

    实例代码:

    <!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 类测试页面 - ShowDivComon 显示弹出层方法</title>
        <!--<script src="https://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript"></script>-->
        <script src="https://files.cnblogs.com/zhangziqiu/ScriptHelperV2.js" type="text/javascript"></script>
        <style type="text/css">
            .cursorHand { cursor:pointer;}        
        </style>  
    </head>
    <body  style="position:relative;">
        <div style="height:300px;"></div>
        <!-- Main Menu -->
        <div >
            <a class="cursorHand" onclick="ScriptHelperV2.showDivCommon(this,'subMenu1');">Menu1</a>
        </div>
        
        <div>
            <select id="Select1" style="z-index:1;">
                <option>123</option>
                <option>456</option>
            </select>
        </div>
        
        <!-- Sub Menu 1 -->
        <div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; 200px;z-index:100;">
            <div>SubMent-1</div>
            <div>SubMent-2</div>
            <div>SubMent-3</div>
            <div>SubMent-4</div>
            <div>SubMent-5</div>
            <div>SubMent-6</div>
        </div>
       </body>
    </html>

    六.打包下载地址

    https://files.cnblogs.com/zhangziqiu/TestScriptHelper_ShowDivComon.rar

    javascript文件下载地址:

    https://files.cnblogs.com/zhangziqiu/ScriptHelperV2.js

    七.相关经验和技巧

    • 一个li对象的width设置为100px或者li的容器ul的width为100px, 在firefox下, li的offsetWidth永远为100, 超出的内容部分不会自动撑开li, 即使设置了overflow:visible仍然只是在li外部显示超出内容. 解决办法是可以li内再增加一个span对象放置内容文字, 这时获取span的offsetWidth为内容的长度而不是li的长度.
    • 使用document.createElement方法创建的对象,在IE6下虽然可以获取到对象, 但是无法再设置他的宽度和高度. 也就是说createElement在IE和FF下均可用, 但是要想动态创建一个标签并且设置他的宽高,比如一个<iframe>, 可以使用写入HTML代码的方式,写入后可以获取对象并设置他的宽度和高度.
    • 一个div,里面有一个iframe, 关闭时一定要先关闭iframe再关闭div, 否则在ie6中iframe会无法关闭.

    八.总结

    这篇文章没有太多技术含量, 高手们请见谅. 在改进中我学习到了新的知识.希望能和鸟儿们一起进步.

    作者:张子秋
    出处:http://www.cnblogs.com/zhangziqiu/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    【Markdown】Markdown相关问题
    【Eclipse】在Project Explore中隐藏不需要显示的文件
    【Eclipse】编译使用Makefile的C工程
    【C】编译提示 warning: incompatible implicit declaration of built-in function ‘calloc’ [enabled by default]
    【Ubuntu】命令记录
    【Vim】Vim学习
    【Ubuntu】安装配置apahce
    【MPI】执行mpiexec出错
    文件上传(java web)
    使用JavaMail发送邮件
  • 原文地址:https://www.cnblogs.com/weekend001/p/1564759.html
Copyright © 2020-2023  润新知