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 相关文章推荐
jquery multiSelect 多选下拉框
Jul 09 Javascript
JavaScript获取表单enctype属性的方法
Apr 02 Javascript
浅谈JavaScript的Polymer框架中的事件绑定
Jul 29 Javascript
微信JSSDK上传图片
Aug 23 Javascript
JavaScript编写简单的计算器
Nov 25 Javascript
深入解析JavaScript框架Backbone.js中的事件机制
Feb 14 Javascript
初步使用bootstrap快速创建页面
Mar 03 Javascript
jquery.validate使用详解
Jun 02 Javascript
JS异步文件分片断点上传的实现思路
Dec 25 Javascript
详解AngularJS 路由 resolve用法
Apr 24 Javascript
vue增加强缓存和版本号的实现方法
May 01 Javascript
基于axios 的responseType类型的设置方法
Oct 29 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
async和DOM Script文件加载比较
2014/07/20 PHP
全面解析PHP操作Memcache基本函数
2016/07/14 PHP
thinkPHP内置字符串截取函数用法详解
2016/11/15 PHP
可以支持多中格式的JS键盘
2007/05/02 Javascript
解析瀑布流布局:JS+绝对定位的实现
2013/05/08 Javascript
JS 精确统计网站访问量的实例代码
2013/07/05 Javascript
jquery二级导航内容均分的原理及实现
2013/08/13 Javascript
javascript实现window.print()去除页眉页脚
2014/12/30 Javascript
JavaScript学习笔记之JS对象
2015/01/22 Javascript
纯JS实现旋转图片3D展示效果
2015/04/12 Javascript
js+html5绘制图片到canvas的方法
2015/06/05 Javascript
浅析AngularJS中的生命周期和延迟处理
2015/06/18 Javascript
有关json_decode乱码及NULL的问题
2015/10/13 Javascript
JS实现的论坛Ajax打分效果完整实例
2015/10/31 Javascript
完美解决浏览器跨域的几种方法(汇总)
2017/05/08 Javascript
详谈DOM简介及节点、属性、查找节点的方法
2017/11/16 Javascript
js实现指定时间倒计时效果
2019/08/26 Javascript
js实现移动端图片滑块验证功能
2020/09/29 Javascript
jQuery实现推拉门效果
2020/10/19 jQuery
[48:22]VGJ.S vs VG 2018国际邀请赛小组赛BO2 第一场 8.16
2018/08/17 DOTA
用Python编写一个国际象棋AI程序
2014/11/28 Python
Python操作MySQL数据库9个实用实例
2015/12/11 Python
django数据库migrate失败的解决方法解析
2018/02/08 Python
Django中日期处理注意事项与自定义时间格式转换详解
2018/08/06 Python
关于Numpy中的行向量和列向量详解
2019/11/30 Python
使用python图形模块turtle库绘制樱花、玫瑰、圣诞树代码实例
2020/03/16 Python
Crocs卡骆驰洞洞鞋日本官方网站:Crocs日本
2016/08/25 全球购物
舒适的豪华鞋:Taryn Rose
2018/05/03 全球购物
中东奢侈品购物网站:Ounass
2020/09/02 全球购物
C语言基础笔试题
2013/04/27 面试题
SQL数据库笔试题
2016/03/08 面试题
表演方阵解说词
2014/02/08 职场文书
公证委托书
2014/08/01 职场文书
工程部文员岗位职责
2015/02/04 职场文书
新党员入党决心书
2015/09/22 职场文书
早上好问候语大全
2015/11/10 职场文书