弹出层插件的编写-layer(跨iframe传值回调)

弹出层(layer)在往上有非常多,这里为什么我要把它的实现提出来,原因有以下2点:

1、写这篇文章也算是我博客的一个开端,他们都说:“不写博客成不了大神” - -

2、我见过的弹出层中都基本没有处理跨iframe传值回调,或者说不是真正意义上的回调函数。

3、一个layer 10K左右就可以完成的功能,非得引用一个jQuery EasyUI、jquery 等等的方式,都是我不能接受的。

 

一个独立的插件应该尽量减少依赖,这也是设计模式中追求的解耦与减少依赖。(layer去依赖某种库或多个库,不是一个很好的选择。)

 

 

先说说调用方式:插件做出来是让人使用的,我觉得从最终的展现形式入手,能让人更直观的了解这个插件是干嘛的。以及它的优点。

 演示代码中我仅仅演示了Iframe弹出层的方式,主要就是看它的回调函数。其它方式,可以在源码中自己查看,稍后将放出源码。

 

调用方式Code:

[html]  view plain  copy
 print ?
  1. <input type="button" onclick="iframe0()" value="iframe层,默认无底部" />  
[html]  view plain  copy
 print ?
  1. <textarea id="callBox" style="height:200px">textarea>  
[javascript]  view plain  copy
 print ?
  1. function iframe0() {  
  2.     layer.win("回调函数的演示""callback.htm"function (data) {  
  3.         document.getElementById("callBox").value += data + "\r\n";  
  4.     })  
  5. }  


上面代码是在index.html页面中的代码,这种场景很多,(比如:订单页面里,需要选择部门的数据,往往会把部门的数据做成一个单独的页面,通过弹出层调用之后选择,然后返回对应的数据)

layer.win是弹出iframe的方式,参数1为弹出层的标题(这个参数其实非常灵活),参数2一看就是一个页面的url,这里设置为callback.html。参数3:这个参数很关键了。它是一个真正意义上的回调函数,也就是当callback.html选择数据返回之后执行的代码。文字比较抽象,还是看代码。

 

[html]  view plain  copy
 print ?
  1. <html xmlns="http://www.w3.org/1999/xhtml">  
  2. <head>  
  3.     <title>title>  
  4.     <script type="text/javascript">  
  5.         function call() {  
  6.             var text = document.getElementById("v1").value;  
  7.             top.layer.callData(location.href, text);  
  8.         }  
  9.     script>  
  10. head>  
  11. <body>  
  12. <h1>注意:这里是在另外一个页面内h1>  
  13. <input type="text" id="v1" />  
  14. <input type="button" value="触发回调函数,真正的父子页面传值" onclick="call()" />  
  15. body>  
  16. html>  


会前端的朋友都能看懂这段代码,非常简单。当点击按钮时:执行了call这个函数,函数内首先获取id为v1的input值,赋值给text变量。然后调用top.layer.callData(location.href, text)

参数1,location.href 即当前的url,这个参数是固定的,不会更改的。参数2 text,就是刚刚获取到的值。

当调用这个方法时:就会执行上面调用layer.win里的回调函数,这是如何办到的,这应该是我们最关心的话题。

我见过许国其它弹出层的处理方式:

[javascript]  view plain  copy
 print ?
  1. function call() {  
  2.     var text = document.getElementById("v1").value;  
[javascript]  view plain  copy
 print ?
  1. parent.document.getElementById("callBox").value=text;            
[javascript]  view plain  copy
 print ?
  1. }  

许多弹出层都是这样的处理方式,或者说 直接parent.xxx(text),xxx即是父页面的一个函数。原理就是通过parent等方式去定位调用之前的页面,然后直接访问其页面的元素或者方法。

那么:这样做到底有什么问题?可以这样说,这种设计违背了设计模式的很多原则,别看JavaScript是弱类型语言。其实它很强大! - -

那到底哪里违背了? 假设我们还是以上面的例子说明:

订单页面里,需要选择部门的数据,当我们选择数据之后执行call时去调用订单页面的,如:parent.document.getElementById("Order-Department").value=data;

单单看上面的代码,貌似也没有什么问题

现在我又有另外一个页面User.html(用户设置页面),同样也需要调用选择部门的页面,首先:我不可能去重写一个部门页面,因为它已经存在了,如果去重写一个,这......我也不好说什么了,如果不重写,那么我们还是调用先前的部门页面,看会发生什么事情。代码可能就会变成这样了:

[javascript]  view plain  copy
 print ?
  1. function call() {  
  2.     var text = document.getElementById("v1").value;  
[javascript]  view plain  copy
 print ?
  1. if(调用页面==部门页面){  
[javascript]  view plain  copy
 print ?
  1. parent.document.getElementById("Order-0|6" right-pos="0|6" space="">Department").value=text;  
[javascript]  view plain  copy
 print ?
  1. }  
[javascript]  view plain  copy
 print ?
  1.   
[javascript]  view plain  copy
 print ?
  1. else if(调用页面==用户页面){  
[javascript]  view plain  copy
 print ?
  1. parent.document.getElementById("User-0|6" right-pos="0|6" space="">background-color: rgb(240, 240, 240);">Department").value=text;       
[javascript]  view plain  copy
 print ?
  1. }  
[javascript]  view plain  copy
 print ?
  1. }  

OK,上面的列子是很常用,很现实的一个例子,如果你是Web系统的开发人员,这种问题基本是不可能不遇到。

很明显:上面的列子违背了太多原则:开放封闭....单一职责原则.......假设还有其它n个页面需要调用到部门选择!假设某天用户页面上的input换了个id!....自己可以想象下!

好了,现在我们回到如何解决这个问题,也就是layer中如何来实现这个真正意义上的回调函数的地方。

翻看源码时你会发现:在调用layer.win方法时,它的第三个参数,也就是回调函数,以这样的代码形式在js中进行了处理:

[javascript]  view plain  copy
 print ?
  1. this.win = function (parameter, src, callback) {  
  2.     if (typeof (parameter) == "object") {  
  3.         layerobj.init(parameter);  
  4.     }  
  5.     else {  
  6.         layerobj.config.title = parameter;  
  7.         layerobj.config.iframe.src = src;  
  8.         layerobj.config.iframe.success = callback;  
  9.     }  
  10.     if (layerobj.config.iframe.success) {  
  11.         window["layergofunc" + layerobj.config.id] = layerobj.config.iframe.success;  
  12.     }  
  13.     layerobj.config.type = 2;  
  14.     layerobj.config.btns.count = 0;  
  15.     layerobj.config.oframe.foot = false;  
  16.     layerobj.run();  
  17.     return layerobj;  
  18. }  


第三个参数callback 以 window["layergofunc" + layerobj.config.id] = layerobj.config.iframe.success的形式进行了保存。它给窗口注册了一个对象,对象名称是一个字符串+时间戳的方式(layerobj.config.id实际是一个时间戳)。

而当回调页面执行parent.layer.callData(location.href, text);时,为什么有一个固定的参数location.href。这个我们参考callData方法,看看它干了什么事情:

[javascript]  view plain  copy
 print ?
  1. layer.callData = function (layerid) {  
  2.     if (layerid && layerid.indexOf("http:") != -1) {  
  3.         layerid = arguments[0].substring(arguments[0].indexOf("layer-id"), arguments[0].length).split("&")[0].split("=")[1];  
  4.     }  
  5.     var temparguments = [].slice.apply(arguments);  
  6.     var temparray = [];  
  7.     if (temparguments.length > 1) {  
  8.         temparray = temparguments.slice(1, temparguments.length);  
  9.     }  
  10.     window["layergofunc" + layerid](temparray);  
  11. };  

首先获取了layerid,通过解析了location.href,这里可以猜得到,在调用win方式时,我们为要调用的页面动态的加了一个参数,键值为layer-id。这里通过获取到这个id,然后重新执行了在win方式时注册的window对象。当执行到最后一句代码window["layergofunc" + layerid](temparray);时,实际上就执行了:function (data) {            document.getElementById("callBox").value += data + "\r\n";        }  这个匿名回调函数。

你可能感兴趣的:(弹出层插件的编写-layer(跨iframe传值回调))