弹出层(layer)在往上有非常多,这里为什么我要把它的实现提出来,原因有以下2点:
1、写这篇文章也算是我博客的一个开端,他们都说:“不写博客成不了大神” - -
2、我见过的弹出层中都基本没有处理跨iframe传值回调,或者说不是真正意义上的回调函数。
3、一个layer 10K左右就可以完成的功能,非得引用一个jquery EasyUI、jquery 等等的方式,都是我不能接受的。
一个独立的插件应该尽量减少依赖,这也是设计模式中追求的解耦与减少依赖。(layer去依赖某种库或多个库,不是一个很好的选择。)
先说说调用方式:插件做出来是让人使用的,我觉得从最终的展现形式入手,能让人更直观的了解这个插件是干嘛的。以及它的优点。
演示代码中我仅仅演示了Iframe弹出层的方式,主要就是看它的回调函数。其它方式,可以在源码中自己查看,稍后将放出源码。
调用方式Code:
function iframe0() {
layer.win("回调函数的演示", "callback.htm", function (data) {
document.getElementById("callBox").value += data + "\r\n";
})
}
上面代码是在index.html页面中的代码,这种场景很多,(比如:订单页面里,需要选择部门的数据,往往会把部门的数据做成一个单独的页面,通过弹出层调用之后选择,然后返回对应的数据)
layer.win是弹出iframe的方式,参数1为弹出层的标题(这个参数其实非常灵活),参数2一看就是一个页面的url,这里设置为callback.html。参数3:这个参数很关键了。它是一个真正意义上的回调函数,也就是当callback.html选择数据返回之后执行的代码。文字比较抽象,还是看代码。
注意:这里是在另外一个页面内
会前端的朋友都能看懂这段代码,非常简单。当点击按钮时:执行了call这个函数,函数内首先获取id为v1的input值,赋值给text变量。然后调用top.layer.callData(location.href, text)
参数1,location.href 即当前的url,这个参数是固定的,不会更改的。参数2 text,就是刚刚获取到的值。
当调用这个方法时:就会执行上面调用layer.win里的回调函数,这是如何办到的,这应该是我们最关心的话题。
我见过许国其它弹出层的处理方式:
function call() {
var text = document.getElementById("v1").value;
parent.document.getElementById("callBox").value=text;
}
许多弹出层都是这样的处理方式,或者说 直接parent.xxx(text),xxx即是父页面的一个函数。原理就是通过parent等方式去定位调用之前的页面,然后直接访问其页面的元素或者方法。
那么:这样做到底有什么问题?可以这样说,这种设计违背了设计模式的很多原则,别看javascript是弱类型语言。其实它很强大! - -
那到底哪里违背了? 假设我们还是以上面的例子说明:
订单页面里,需要选择部门的数据,当我们选择数据之后执行call时去调用订单页面的,如:parent.document.getElementById("Order-Department").value=data;
单单看上面的代码,貌似也没有什么问题
现在我又有另外一个页面User.html(用户设置页面),同样也需要调用选择部门的页面,首先:我不可能去重写一个部门页面,因为它已经存在了,如果去重写一个,这......我也不好说什么了,如果不重写,那么我们还是调用先前的部门页面,看会发生什么事情。代码可能就会变成这样了:
function call() {
var text = document.getElementById("v1").value;
if(调用页面==部门页面){
parent.document.getElementById("Order-Department").value=text;
}
else if(调用页面==用户页面){
parent.document.getElementById("User-Department").value=text;
}
}
OK,上面的列子是很常用,很现实的一个例子,如果你是Web系统的开发人员,这种问题基本是不可能不遇到。
很明显:上面的列子违背了太多原则:开放封闭....单一职责原则.......假设还有其它n个页面需要调用到部门选择!假设某天用户页面上的input换了个id!....自己可以想象下!
好了,现在我们回到如何解决这个问题,也就是layer中如何来实现这个真正意义上的回调函数的地方。
翻看源码时你会发现:在调用layer.win方法时,它的第三个参数,也就是回调函数,以这样的代码形式在js中进行了处理:
this.win = function (parameter, src, callback) {
if (typeof (parameter) == "object") {
layerobj.init(parameter);
}
else {
layerobj.config.title = parameter;
layerobj.config.iframe.src = src;
layerobj.config.iframe.success = callback;
}
if (layerobj.config.iframe.success) {
window["layergofunc" + layerobj.config.id] = layerobj.config.iframe.success;
}
layerobj.config.type = 2;
layerobj.config.btns.count = 0;
layerobj.config.oframe.foot = false;
layerobj.run();
return layerobj;
}
第三个参数callback 以 window["layergofunc" + layerobj.config.id] = layerobj.config.iframe.success的形式进行了保存。它给窗口注册了一个对象,对象名称是一个字符串+时间戳的方式(layerobj.config.id实际是一个时间戳)。
而当回调页面执行parent.layer.callData(location.href, text);时,为什么有一个固定的参数location.href。这个我们参考callData方法,看看它干了什么事情:
layer.callData = function (layerid) {
if (layerid && layerid.indexOf("http:") != -1) {
layerid = arguments[0].substring(arguments[0].indexOf("layer-id"), arguments[0].length).split("&")[0].split("=")[1];
}
var temparguments = [].slice.apply(arguments);
var temparray = [];
if (temparguments.length > 1) {
temparray = temparguments.slice(1, temparguments.length);
}
window["layergofunc" + layerid](temparray);
};
首先获取了layerid,通过解析了location.href,这里可以猜得到,在调用win方式时,我们为要调用的页面动态的加了一个参数,键值为layer-id。这里通过获取到这个id,然后重新执行了在win方式时注册的window对象。当执行到最后一句代码window["layergofunc" + layerid](temparray);时,实际上就执行了:function (data) { document.getElementById("callBox").value += data + "\r\n"; } 这个匿名回调函数。
刚写博文,风格不好,思路也比较乱。不知道有没有解释清楚iframe传值原理。源码我已经提供了下载。这个layer还有很多其它的功能,如果有人有时间做做美化之类的,用它在实际项目中使用来代替模态窗口是个非常不错的选择。首先它小巧,遵循了许多设计上的原则!欢迎留言批判!!!
纯js弹出层附测试demo