Robin's Blog

记录 积累 学习 成长

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

一.摘要

本篇文章并没有为系列文中构造的轻量级脚本库添加新的方法, 而是改进了原有弹出浮动层的方法. 对方法中获取位置的函数重构出来, 为弹出层自动添加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-width: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; width: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会无法关闭.
0
0
(请您对文章做出评价)
posted on 2010-01-21 10:35  Robin99  阅读(1099)  评论(0编辑  收藏  举报