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 插件 web2.0分格的分页脚本,可用于ajax无刷新分页
Dec 25 Javascript
JavaScript 继承详解(一)
Jul 13 Javascript
JQuery 学习笔记 选择器之三
Jul 23 Javascript
基于jQuery的前端数据通用验证库
Aug 08 Javascript
鼠标滚轮控制网页横向移动实现思路
Mar 22 Javascript
Javascript中的回调函数和匿名函数的回调示例介绍
May 12 Javascript
简介JavaScript中search()方法的使用
Jun 06 Javascript
js轮盘抽奖实例分析
Apr 17 Javascript
js自定义Tab选项卡效果
Jun 05 Javascript
使用JS实现图片轮播的实例(前后首尾相接)
Sep 21 Javascript
浅谈vux之x-input使用以及源码解读
Nov 04 Javascript
详解Vue组件之作用域插槽
Nov 22 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函数(ignore_user_abort)
2012/08/01 PHP
使用PHP备份MySQL和网站发送到邮箱实例代码
2013/11/28 PHP
微信支付开发教程(一)微信支付URL配置
2014/05/28 PHP
编写PHP脚本来实现WordPress中评论分页的功能
2015/12/10 PHP
php自动提交表单的方法(基于fsockopen与curl)
2016/05/09 PHP
利用jQuery的deferred对象实现异步按顺序加载JS文件
2013/03/17 Javascript
jQuery向后台传入json格式数据的方法
2015/02/13 Javascript
js实现时间显示几天前、几小时前或者几分钟前的方法集锦
2015/05/29 Javascript
js去除浏览器默认底图的方法
2015/06/08 Javascript
js HTML5多图片上传及预览实例解析(不含前端的文件分割)
2016/08/26 Javascript
vue.js学习之递归组件
2016/12/13 Javascript
js实现图片切换(动画版)
2016/12/25 Javascript
Angualrjs和bootstrap相结合实现数据表格table
2017/03/30 Javascript
阿里大于短信验证码node koa2的实现代码(最新)
2017/09/07 Javascript
使用axios实现上传图片进度条功能
2017/12/21 Javascript
vue.js使用v-if实现显示与隐藏功能示例
2018/07/06 Javascript
iview tabs 顶部导航栏和模块切换栏的示例代码
2019/03/04 Javascript
使用JavaScript实现网页秒表功能(含开始、暂停、继续、重置功能)
2020/06/05 Javascript
js实现点击选项置顶动画效果
2020/08/25 Javascript
利用python将图片转换成excel文档格式
2017/12/30 Python
Pytorch 多块GPU的使用详解
2019/12/31 Python
通过实例了解Python str()和repr()的区别
2020/01/17 Python
Jupyter notebook 远程配置及SSL加密教程
2020/04/14 Python
全天然狗零食:Best Bully Sticks
2016/09/22 全球购物
IFCHIC台湾:欧美国际设计师品牌
2019/05/18 全球购物
购房意向书范本
2014/04/01 职场文书
企业文化宣传标语
2014/06/09 职场文书
2014年党的群众路线教育实践活动整改措施(个人版)
2014/09/25 职场文书
银行授权委托书格式
2014/10/10 职场文书
2014年行风建设工作总结
2014/12/01 职场文书
英语邀请函范文
2015/02/02 职场文书
2015医院个人工作总结范文
2015/05/21 职场文书
暖春观后感
2015/06/08 职场文书
赵氏孤儿观后感
2015/06/09 职场文书
余世维讲座观后感
2015/06/11 职场文书
用 Python 定义 Schema 并生成 Parquet 文件详情
2021/09/25 Python