js实现模拟弹层,效果图如下:
- 这个模式弹窗是我结合Jquery写的,因为有时候用Jquery的时候会造成和其他脚本有冲突。。。
- 感觉Jquery的有些插件也不能都满足所有的需求,有些Bug不好怎么去解决;
- 我自己写的也还有很多BUG,但是自己写的的也好去修复它,功能如下:
- 同样支持窗体缩放大小时自动居中对齐(IE下必须很秀气的resize才能正确触发);
- 支持同时打开多个窗体,并能响应Esc事件和点击遮掩层进行关闭弹窗;
- 还有很对功能待完善,目前已经加入了拖拽和缩放大小的功能,默认是false不拖拽的,在init里面可以设置drag:true以让元素可拖拽,拖拽的代码是我之前在51js上看到别人贴的,很简洁所以就直接拿过来用了,但是ie内还是有些拖拽的Bug需要修复。
最新源代码如下:
Popup静态类
同时打开多个弹窗
其实我不知道这个功能实不实用,项目中有没有此需求,但是当时想做这个功能的时候我还是想支持多个弹窗,每个弹窗的大小或位置相互独立,互不影响,所以要做到这点,还不如完全支持多弹窗功能。但如果要对多个弹窗进行管理的话我想使用propotype不是很好,每一次创建一个弹窗就new Popup()?还是用一个静态类,里面放一个Array数组保存所有预载弹窗对象,根据getInstanceById方法根据初始化指定的Id获取弹窗,切换当前要操作的弹窗,返回this对象后就可直接再调用它的open、close等方法。
遮掩的背景层
这个问题磨我很长时间,是这样,我每打开一个弹窗的时候就需要创建对应的透明overlay层,打开的弹窗是浮在这个遮掩的div层上面的,这个层的高度=document高度、宽度=document宽度,而弹窗是绝对定位并自动居中,top=(window高度-自身高度)/2+滚动条高度,left=(window宽度-自身宽度)/2,如果要做到浏览器兼容,就需要把获取宽度和高度信息的方法独立出来,我这里叫global,里面获取宽度、高度、浏览器的方法主要是参考的jQuery,但发现了它的一个bug,是在opera浏览器里测试发现的,使用jQuery的那套方法获取window的高度不正确,不信可以在opera浏览器里面调用$(window).height()方法,另外还有获取文挡的高度$(document).width()方法在opera里也不正确,返回的都比实际值偏高。为此我在global的objProperty静态方法都都加入了this.browser.opera && name=="Width"或"Height"的特殊判断,这样我设置遮掩层的高度和宽度就是document高度和document宽度,保证是document的最大宽高度,能全部遮挡住整个页面,但又不超过当前的document的最大宽高度,不至于使页面产生滚动条。当然还有一个调用上的问题是:一定要在document完全load完后才调用获取document的宽高度的方法,如果在document还未解析完获取的就只是当前已加载的document宽高度,而加载完成后了的document宽高度就肯定不一样了。
ie的z-index问题
众所周之,ie是众多浏览器里bug最多的一个,特别是ie6里有一些老大难的棘手问题,但是我们不能因为这一点点小的困难而放弃,想当年ie独占浏览器市场风光一时,那时候我想没有人骂ie吧,现在也就不能因为有Firefox、Chrome更多优秀的浏览器而埋怨它,就算是再优秀的浏览器只要是软件就有bug,我在ff的2.0版本里就发现过一个bug,我们也不能强制用户一定要到升级到更高浏览器版本,这样好象可能会违背用户的意愿,就像是作为一个程序开发员的我,就会有一种这样的逆反心理:当访问某一个网站被跳转提示说不能使用ie6浏览时,除开内容非看不可,我宁愿不看也不愿再多点下打开firefox浏览器去看,因为这样我会感觉非常的不爽。回到ie的问题上,ie里select不能被任何除开object和iframe元素遮挡,而iframe能被div遮挡,所以比较好的做法是先创建一个一样大的iframe遮挡住document,再创建一个div遮挡住iframe,有时候我们也可以绕开它的bug去解决它,我这里为了图方便就直接把页面所有的select隐藏掉,display:none或visibility:visible都能隐藏掉元素,但visibility:visible还能占位使页面布局保持不变,如果不想for循环页面所有select可以在css里预定义一个hideselect类{visibility:visible},然后动态给body的className加上hideselect即可。另外ie的z-index还有一些潜规则,我在做position为absolute绝对定位层的时候发现这个问题,当有多个的浮动层的时候设置z-index可能无效,后来网上找了些资料发现ie的z-index是有stacking context这个因素的影响,不过元素处于同一级stacking level则由z-index决定,所以我在open()方法里加上判断if( target.parentNode != document.body ) document.body.insertBefore(target, document.body.childNodes[0]);把所有的弹窗层全部移动到body元素下面,仅多加上这一句就可以让ie乖乖的听z-index控制,所以说ie的问题还是好解决的嘛,发现我瞒喜欢沉浸在解决ie的一些css的bug之中。
拖拽和缩放大小
在我Popup类有个eDrag方法用于实现元素的拖拽,有人曾说我封装不完成,事件没释放,确实好象存在这个问题,应该要把拖拽元素和要拖拽的句柄元素作为参数一并传递过来,不过先不管了,最重要的是简单方便调用就行了,没释放消耗着的是客户的内存,他也不知道关系。不过这个eDrag拖拽元素方法是网上比较流行,因为就这几行简单的代码兼容行很好,缩放大小跟拖拽类似,只不过是坐标加减时参数不一样,但如果在拖拽的元素中包含iframe会很"卡",不管是在那种浏览器中,我觉得造成这种很"卡"的显示不是脚本有性能的问题,而是在你拖拽中浏览器要不断重绘渲染元素,这个过程很耗资源,特别是有iframe的时候,如果缩放大小时拖动太快会导致鼠标会偏移,如果跑到iframe里去就完了,鼠标移到这里都没办法结束拖拽。曾试着让拖拽的句柄元素覆盖在iframe之上,ie内可以解决问题,但ff内空的div覆盖iframe上不行,鼠标移上去还是算的iframe,后来干脆把iframe隐藏,就算鼠标位置偏移了拖拽也没问题,至少还是本页面元素,想一想其实这样做是对的,网上一般处理拖拽也是clone个div层只描绘出一个边框跑,看下window的窗体拖拽不也是拖着一个空框框。
以后的扩展
在调用create方法初始化弹窗时,会在body下面创建个div弹窗层,如果是iframe那么也会被加载,就算从来没打开过弹窗但iframe也预载了,如果页面真的好有几个iframe的弹窗就会很慢,并且有点浪费资源,我在想是否初始化创建时候指定src初始值为about blank,然后在open里加多个可选参数refresh,用于刷新iframe的路径或重新指定新的一个地址。
对弹窗定位,目前我都默认左右对齐上下居中,并且在弹窗打开的时候监视window的resize事件,自动对齐居中。我在想再监视一个scroll拉滚动条事件,然后在settings里加2个vertical和align属性设置对齐方式,并加个开关可设置遮掩的背景层是否显示,让弹窗可以浮动在页面的某一个位置,这样不就又多种功能吗?
结束语:
这个算是我使用jQuery后写的第一个较正式的js类库,08年开始接触jQuery,就被它的dom筛选器给吸引,但老实说还未能全看明白它的源代码,有次自己写js的时候发现和jQuery有点冲突,所以就决定以后不想再依赖jQuery这个重量级的框架,本身它的脚本文件也不小,而我们用到的只是小部分功能,如果要自己写js来实现,像dom筛选器的取一个列表的,我一般会在最外面的容器加个id,然后尽量不使用class做为条件,使用childNodes、getElementsByTagName获取元素,如果需要的话再加上一个nodeType!=3空元素的判断,自己可以做个简单的函数封装下,基本上也能满足需求。像是Ajax,其实自己写几行代码就搞定了,用着这么麻烦依赖别人的框架吗?所以还是支持大家自己动手,Do it yourself!