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

来源:互联网 发布:矩阵通解 编辑:程序博客网 时间:2024/04/30 07:11

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

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

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

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

 

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

 

 

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

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

 

调用方式Code:

<input type="button" onclick="iframe0()" value="iframe层,默认无底部" />
<textarea id="callBox" style="height:200px"></textarea>
    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选择数据返回之后执行的代码。文字比较抽象,还是看代码。

 

<html xmlns="http://www.w3.org/1999/xhtml"><head>    <title></title>    <script type="text/javascript">        function call() {            var text = document.getElementById("v1").value;            top.layer.callData(location.href, text);        }    </script></head><body><h1>注意:这里是在另外一个页面内</h1><input type="text" id="v1" /><input type="button" value="触发回调函数,真正的父子页面传值" onclick="call()" /></body></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(调用页面==部门页面){
            <strong>parent.document.getElementById("Order-<span left-pos="0|6" right-pos="0|6" space="">Department</span>").value=text;</strong>
    }
 
    else if(调用页面==用户页面){
            <strong>parent.document.getElementById("User-<span left-pos="0|6" right-pos="0|6" space=""><span style="background-color: rgb(240, 240, 240);">Department</span></span>").value=text;</strong>     
    }
        }

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
 

1 0
原创粉丝点击