Javascript公共脚本库系列(一): 弹出层脚本


Posted in Javascript onFebruary 24, 2011

一.摘要
本系列文章是为了抽象通用的,跨浏览器的脚本方法.

本篇文章讲解弹出浮动层的javascript函数, 以及函数的原理和使用注意事项.

二.实现效果
用脚本弹出浮动层是我们最常用的脚本方法之一.下面是效果图:

Javascript公共脚本库系列(一): 弹出层脚本 
点击图中的"航空公司"后,会在"航空公司"下面弹出浮动层.

在网上弹出框的脚本相当多, 而且还有各种第三方JS框架可供我们使用.但是其中有的脚本过于简单,仅仅粗略的实现弹出效果而忽略了灵活性,通用性和跨浏览器特性. 使用JS框架又有些杀鸡用牛刀.所以在收集整理了一些资料后, 写出了下文中的ScriptHelper类的弹出层方法.

主要特点有:
支持多浏览器
使用面向对象方法封装
使用简单,通用性强.
将计算位置等函数进行提取, 所有的相关函数都可以单独调用, 可根据具体项目继续二次开发.
三.脚本方法
下面我先将脚本方法贡献出来,然后举例如何使用. 最后讲解脚本的原理.

/* ==================== ScriptHelper 开始 ==================== */ 
/* scriptHelper 脚本帮助对象. 
创建人: ziqiu.zhang 2008.3.5 
添加函数: 
getScroll():得到鼠标滚过的距离-兼容XHTML 
getClient():得到浏览器当前显示区域的大小-兼容XHTML 
showDivCommon():显示图层. 
使用举例: 
<div id="testDiv" style="display:none; position:absolute; border:1px #000000;">我是测试图层我是测试图层</div> 
<div style="width:400px; text-align:center;"><div><a href="#" onclick="ScriptHelper.showDivCommon(this,'testDiv', 20, 70)">事件源</a></div></div> 
*/ 
function scriptHelper() 
{ 
} // 得到鼠标滚过的距离 scrollTop 与 scrollLeft 
/* 用法与测试: 
var myScroll = getScroll(); 
alert("myScroll.scrollTop:" + myScroll.scrollTop); 
alert("myScroll.scrollLeft:" + myScroll.scrollLeft); 
*/ 
scriptHelper.prototype.getScroll = function () 
{ 
var scrollTop = 0, scrollLeft = 0; 
scrollTop = (document.body.scrollTop > document.documentElement.scrollTop)? document.body.scrollTop:document.documentElement.scrollTop; 
if( isNaN(scrollTop) || scrollTop <0 ){ scrollTop = 0 ;} 
scrollLeft = (document.body.scrollLeft > document.documentElement.scrollLeft )? document.body.scrollLeft:document.documentElement.scrollLeft; 
if( isNaN(scrollLeft) || scrollLeft <0 ){ scrollLeft = 0 ;} 
return { scrollTop:scrollTop, scrollLeft: scrollLeft}; 
} 
// 得到浏览器当前显示区域的大小 clientHeight 与 clientWidth 
/* 用法与测试: 
var myScroll = getScroll(); 
alert("myScroll.sTop:" + myScroll.sTop); 
alert("myScroll.sLeft:" + myScroll.sLeft); 
*/ 
scriptHelper.prototype.getClient = function () 
{ 
//判断页面是否符合XHTML标准 
var isXhtml = true; 
if( document.documentElement == null || document.documentElement.clientHeight <= 0) 
{ 
if( document.body.clientHeight>0 ) 
{ 
isXhtml = false; 
} 
} 
this.clientHeight = isXhtml?document.documentElement.clientHeight:document.body.clientHeight; 
this.clientWidth = isXhtml?document.documentElement.clientWidth:document.body.clientWidth; 
return {clientHeight:this.clientHeight,clientWidth:this.clientWidth}; 
} 
// 显示图层,再次调用则隐藏 
/* 参数说明: 
sObj : 要弹出图层的事件源 
divId : 要显示的图层ID 
sObjHeight : 事件源的高度,默认为20.需要手工传入是因为对于由于事件源对象可能是各种HTML元素,有些元素高度的计算无法跨浏览器通用. 
moveLeft : 手工向左移动的距离.不移动则为0(默认). 
divObjHeight: 弹出层的高度.如果传入大于0的此参数, 则当事件源下方空间不足时,在事件源上方弹出层.如果不传此参数则一直在事件源下方弹出. 
用法与测试: 
<div><a href="#" onclick="ScriptHelper.showDivCommon(this,'testDiv', 20, 20)">事件源</a></div> 
*/ 
scriptHelper.prototype.showDivCommon = function (sObj,divId, sObjHeight, moveLeft, divObjHeight) 
{ 
//取消冒泡事件 
if( typeof(window)!='undefined' && window != null && window.event != null ) 
{ 
window.event.cancelBubble = true; 
} 
else if( ScriptHelper.showDivCommon.caller.arguments[0] != null ) 
{ 
ScriptHelper.showDivCommon.caller.arguments[0].cancelBubble = true; 
} 
//参数检测.如果没有传入参数则设置默认值 
if( moveLeft == null ) 
{ 
moveLeft = 0; 
} 
if( sObjHeight == null ) 
{ 
sObjHeight = 20; 
} 
if(divObjHeight == null) 
{ 
divObjHeight = 0; 
} 
var divObj = document.getElementById(divId); //获得图层对象 
var sObjOffsetTop = 0; //事件源的垂直距离 
var sObjOffsetLeft = 0; //事件源的水平距离 
var myClient = this.getClient(); 
var myScroll = this.getScroll(); 
var sWidth = sObj.width; //事件源对象的宽度 
var sHeight = sObjHeight; //事件源对象的高度 
var bottomSpace; //距离底部的距离 
/* 获取事件源控件的高度和宽度.*/ 
if( sWidth == null ) 
{ 
sWidth = 100;//无法获取则为100 
} 
else 
{ 
sWidth = sWidth + 1; //留出1px的距离 
} 
if( divObj.style.display.toLowerCase() != "none" ) 
{ 
//隐藏图层 
divObj.style.display = "none"; 
} 
else 
{ 
if( sObj == null ) 
{ 
alert("事件源对象为null"); 
return false; 
} 
/* 获取事件源对象的偏移量 */ 
var tempObj = sObj; //用于计算事件源坐标的临时对象 
while( tempObj && tempObj.tagName.toUpperCase() != "BODY" ) 
{ 
sObjOffsetTop += tempObj.offsetTop; 
sObjOffsetLeft += tempObj.offsetLeft; 
tempObj = tempObj.offsetParent; 
} 
tempObj = null; 
/* 获取距离底部的距离 */ 
bottomSpace = parseInt(myClient.clientHeight) - ( parseInt(sObjOffsetTop) - parseInt(myScroll.scrollTop)) - parseInt(sHeight); 
/* 设置图层显示位置 */ 
//如果事件源下方空间不足且上方控件足够容纳弹出层,则在上方显示.否则在下方显示 
if( divObjHeight>0 && bottomSpace < divObjHeight && sObjOffsetTop >divObjHeight ) 
{ 
divObj.style.top = ( parseInt( sObjOffsetTop ) - parseInt( divObjHeight ) - 10).toString() + "px"; 
} 
else 
{ 
divObj.style.top = ( parseInt( sObjOffsetTop ) + parseInt( sHeight ) ).toString() + "px"; 
} 
divObj.style.left = ( parseInt( sObjOffsetLeft ) - parseInt( moveLeft ) ).toString() + "px"; 
divObj.style.display="block"; 
} 
} 
// 关闭图层 
/* 参数说明: 
divId : 要隐藏的图层ID 
用法与测试: 
ScriptHelper.closeDivCommon('testDiv'); 
*/ 
scriptHelper.prototype.closeDivCommon = function (divId) 
{ 
// 
var divObj = document.getElementById(divId); //获得图层对象 
if( divObj != null ) 
{ 
divObj.style.display = "none"; 
} 
} 
//建立scriptHelper类的一个实例对象.全局使用. 
var ScriptHelper = new scriptHelper(); 
/* ==================== ScriptHelper 结束 ==================== */

四.使用举例
接下来我们创建HTML页面演示如何使用此脚本.此实例是一个菜单,当点击时显示子菜单图层.
1.引用脚本文件
将上面的代码保存在ScriptHelper.js文件中.在页面中添加引用:
<script src="http://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript" defer="defer"></script>
2.编写子菜单
先编写两个子菜单图层.
<!-- Sub Menu 1 --> 
<div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; height:100px;"> 
<div>1-1</div> 
<div>1-2</div> 
</div> 
<!-- Sub Menu 2 --> 
<div id="subMenu2" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; padding:5px;" > 
<div>2-1</div> 
<div>2-2</div> 
</div>

对于子菜单, 最重要的就是要设置两个样式:position和display.
position:absolute 让此图层能够精确定位显示.从而控制他的显示位置.
display:none 让图层在加载时不显示.
3.编写主菜单
主菜单代码如下:
<!-- Main Menu --> 
<div > 
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu1', 20, 0, 100)">Menu1</a> 
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu2', 20, 0)">Menu2</a> 
<a class="cursorHand" href="#">NoSubMenu</a> 
</div>

我们创建了三个菜单.其中Menu1和Menu2拥有子菜单, NoSubMenu没有子菜单.
我们使用了a元素创建菜单对象, 但是因为没有为其添加href属性,所以默认情况下鼠标放上去不会变成hand图形.需要为其添加样式cursorHand,次样式的代码如下:
<style type="text/css">
.cursorHand { cursor:pointer;}
</style>
最关键的是为菜单添加的onclick事件, 此事件调用ScriptHelper.showDivCommon方法用于显示图层.
方法第一个参数值为this表示将事件源对象传入, 在函数中会根据事件源计算显示位置.
方法第二个参数表示弹出图的Id
方法第三个参数是可选参数, 用于设置向下的偏移量.因为我们计算的位置是"<a>Menu1</a>"这个元素的左上角坐标.需要设置一个向下的偏移量.一般设置为事件源的高度,默认为20px.
方法第四个参数是可选参数,用于设置向左的偏移量.原因同上.默认为0px;
方法第五个参数是可选参数,需要传入弹出层的高度.如果使用了此属性则弹出层可能弹出在事件源上方.不传递此属性则始终在事件源下方弹出图层.
4.效果与完整代码

Javascript公共脚本库系列(一): 弹出层脚本 
完整实例代码如下:

<!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 类测试页面</title> 
<script src="http://files.cnblogs.com/zhangziqiu/ScriptHelper.js" type="text/javascript" defer="defer"></script> 
<style type="text/css"> 
.cursorHand { cursor:pointer;} 
</style> 
</head> 
<body style="position:relative;"> 
<div style="height:200px;"></div> 
<!-- Main Menu --> 
<div > 
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu1', 20, 0, 100)">Menu1</a> 
<a class="cursorHand" onclick="ScriptHelper.showDivCommon(this,'subMenu2', 20, 0)">Menu2</a> 
<a class="cursorHand" href="#">NoSubMenu</a> 
</div> 
<!-- Sub Menu 1 --> 
<div id="subMenu1" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; margin:0px; padding:5px; height:100px;"> 
<div>1-1</div> 
<div>1-2</div> 
</div> 
<!-- Sub Menu 2 --> 
<div id="subMenu2" style="position:absolute; display:none; background-color:#D7EFCD; border:solid 1px #000000; padding:5px;" > 
<div>2-1</div> 
<div>2-2</div> 
</div> 
</body> 
</html>

五.注意事项:
1.要给Body元素加上position:relative样式:
<body style="position:relative;">
不增加的话在IE6下有时会出现无法精确定位事件源的情况.比如在menu的<div>上添加几个<br/>,则弹出层的位置就发生错误了.
如果在Body元素上增加此样式仍然弹出位置错误,则请在事件源对象的容器元素中也添加此样式
2.不传递最后一个参数则弹出层只在事件源下面弹出.否则将会计算事件源底部的举例, 如果底部控件不足并且上部控件充足,则弹出层显示在事件源上方.
3.在页面上要添加DOCTYPE元素.不添加的话很可能在某些浏览器中出现错误.有关DOCTYPE的作用,请查看下面的文章:
DOCTYPE元素解析
六.总结
兼容多浏览器真的是一件让人头疼的事情.我估计此函数还是会有问题.本来想写脚本分析的, 但是在写作的时候又发现了一些Bug并且进行了修正.兼容来兼容去最后把自己兼容晕了.其实如果一个项目能使用脚本库将会是一个很好的选择.本系列文章只是希望构建一个轻量级的脚本类库.大家使用中有任何问题希望多多交流, 一起打造简单易用的脚本库!
Javascript 相关文章推荐
js对象关系图 方便dom操作
Mar 18 Javascript
如何使用jQUery获取选中radio对应的值(一句代码)
Jun 03 Javascript
JavaScript利用正则表达式去除日期中的“-”
Jul 01 Javascript
JavaScript控制listbox列表框的项目上下移动的方法
Mar 18 Javascript
jquery仅用6行代码实现滑动门效果
Sep 07 Javascript
倾力总结40条常见的移动端Web页面问题解决方案
May 24 Javascript
妙用Bootstrap的 popover插件实现校验表单提示功能
Aug 29 Javascript
强大Vue.js组件浅析
Sep 12 Javascript
layui弹出层效果实现代码
May 19 Javascript
Node.js原生api搭建web服务器的方法步骤
Feb 15 Javascript
JavaScript数组、json对象、eval()函数用法实例分析
Feb 21 Javascript
JS扁平化输出数组的2种方法解析
Sep 17 Javascript
从零开始学习jQuery (十一) 实战表单验证与自动完成提示插件
Feb 23 #Javascript
从零开始学习jQuery (十) jQueryUI常用功能实战
Feb 23 #Javascript
从零开始学习jQuery (八) 插播:jQuery实施方案
Feb 23 #Javascript
从零开始学习jQuery (六) jquery中的AJAX使用
Feb 23 #Javascript
从零开始学习jQuery (四) jQuery中操作元素的属性与样式
Feb 23 #Javascript
从零开始学习jQuery (三) 管理jQuery包装集
Feb 23 #Javascript
jQuery的学习步骤
Feb 23 #Javascript
You might like
PHP新手上路(三)
2006/10/09 PHP
MySQL修改密码方法总结
2008/03/25 PHP
PHP自动补全表单的两种方法
2017/03/06 PHP
Thinkphp开发--集成极光推送
2017/09/15 PHP
Laravel 实现密码重置功能
2018/02/23 PHP
封装好的省市地区联动控件附下载
2007/08/13 Javascript
javascript 定义初始化数组函数
2009/09/07 Javascript
javascript 图片裁剪技巧解读
2012/11/15 Javascript
Query中click(),bind(),live(),delegate()的区别
2013/11/19 Javascript
JS获取url链接字符串 location.href
2013/12/23 Javascript
一个网页标题title的闪动提示效果实现思路
2014/03/22 Javascript
js实现checkbox全选和反选示例
2014/05/01 Javascript
jquery调取json数据实现省市级联的方法
2015/01/29 Javascript
基于jQuery实现的双11天猫拆红包抽奖效果
2015/12/01 Javascript
jQuery实现二级下拉菜单效果
2016/01/05 Javascript
使用grunt合并压缩js和css文件的方法
2017/03/02 Javascript
JS实现获取图片大小和预览的方法完整实例【兼容IE和其它浏览器】
2017/04/24 Javascript
如何理解Vue的render函数的具体用法
2017/08/30 Javascript
video.js 一个页面同时播放多个视频的实例代码
2018/11/27 Javascript
JavaScript模板引擎实现原理实例详解
2018/12/14 Javascript
使用zrender.js绘制体温单效果
2019/10/31 Javascript
微信小程序完美解决scroll-view高度自适应问题的方法
2020/08/08 Javascript
Python压缩和解压缩zip文件
2015/02/14 Python
python使用smtplib模块通过gmail实现邮件发送的方法
2015/05/08 Python
Python一个简单的通信程序(客户端 服务器)
2019/03/06 Python
Mac安装python3的方法步骤
2019/08/09 Python
python PIL/cv2/base64相互转换实例
2020/01/09 Python
Python爬虫HTPP请求方法有哪些
2020/06/03 Python
一文带你了解Python 四种常见基础爬虫方法介绍
2020/12/04 Python
一站式跨境收款解决方案:Payoneer(派安盈)
2018/09/06 全球购物
NET程序员上机面试题
2015/05/23 面试题
2015年党支部公开承诺书
2015/01/22 职场文书
2015教师个人工作总结范文
2015/03/31 职场文书
python numpy中multiply与*及matul 的区别说明
2021/05/26 Python
python中opencv实现图片文本倾斜校正
2021/06/11 Python
python index() 与 rindex() 方法的使用示例详解
2022/12/24 Python