FrameController.js – 优雅的处理单页多框架(iframe)窗口管理同步问题

栏目: Html · 发布时间: 7年前

内容简介:做后台时,不可避免的会遇到 内嵌 iframe 的情况。最近的 一个项目 有客户反馈无法保存,提示 token 错误,。经过沟通发现 是因为打开了 多个内嵌页(iframe),会出现此问题(使用ThinkPHP5自带方法 token(),每次调用都会生成新的Token)。修复方法也很简单,直接在 新增内嵌页时,将新生成的token进行广播。这种情况在 后台 开发中会经常遇到,把 代码 提取出来,做了一下简单的封装,做成开源项目(考虑到后台可能要兼容非常老的平台,特别针对 IE7、IE8 做了兼容处理,基本

做后台时,不可避免的会遇到 内嵌 iframe 的情况。最近的 一个项目 有客户反馈无法保存,提示 token 错误,。经过沟通发现 是因为打开了 多个内嵌页(iframe),会出现此问题(使用ThinkPHP5自带方法 token(),每次调用都会生成新的Token)。修复方法也很简单,直接在 新增内嵌页时,将新生成的token进行广播。

这种情况在 后台 开发中会经常遇到,把 代码 提取出来,做了一下简单的封装,做成开源项目( https://gitee.com/mqycn/FrameController.js )。

考虑到后台可能要兼容非常老的平台,特别针对 IE7、IE8 做了兼容处理,基本可以实现所有浏览器的兼容。运行效果如下:

FrameController.js – 优雅的处理单页多框架(iframe)窗口管理同步问题

var addLog = function(from, event, data) {
    var _old = $('#log').html().substring(0, 3000);
    $('#log').html(
        (logTpl + _old)
        .replace('#EVENT#', event)
        .replace('#DATA#', JSON.stringify(data))
        .replace('#SOURCE#', from)
    );
    console.log('event:', event, 'data:', data);
};

//同步通知
FrameController.addListener('broadcast', function(e) {
    $('#msg').val(e.data.msg);
    addLog(e.frameId, e.event, e.data);
});

//发送广播
$('#send').click(function() {
    var nums = FrameController.broadcast('broadcast', {
        msg: $('#msg').val()
    });
    $('#log').html('通知成功:' + nums + '\n\n' + $('#log').html());
});

//更新输入状态
$('#msg').change(function() {
    FrameController.broadcast('change', {
        text: '输入框内容已更改:' + $(this).val()
    });
});

//更新状态
FrameController.addListener('change', function(e) {
    addLog(e.frameId, e.event, e.data);
});

FrameController.js – 优雅的处理单页多框架(iframe)窗口管理同步问题

对于 新增窗口、关闭窗口和刷新页面,会通过 进行广播

var addLog = function(from, event, data) {
    var _old = $('#log').html().substring(0, 3000);
    $('#log').html(
        (logTpl + _old)
        .replace('#EVENT#', event)
        .replace('#DATA#', JSON.stringify(data))
        .replace('#SOURCE#', from)
    );
    console.log('event:', event, 'data:', data);
};

//监听系统事件
FrameController.addListener('frame.remove', function(e) {
    addLog(e.frameId, e.event, e.data);
});
FrameController.addListener('frame.add', function(e) {
    addLog(e.frameId, e.event, e.data);
});

FrameController.js – 优雅的处理单页多框架(iframe)窗口管理同步问题

var logTpl = '事件:#EVENT# 来源:#SOURCE#\n数据:#DATA#\n\n',
    addLog = function(from, event, data) {
        var _old = $('#log').html().substring(0, 3000);
        $('#log').html(
            (logTpl + _old)
            .replace('#EVENT#', event)
            .replace('#DATA#', JSON.stringify(data))
            .replace('#SOURCE#', from)
        );
        console.log('event:', event, 'data:', data);
    },
    msgEventListener = function(e) {
        $('#log').html('自定义事件已经触发,添加多次会触发多次\n\n' + $('#log').html());
    };


//添加自定义事件
$('#add_custom').click(function() {
    FrameController.addListener('broadcast', msgEventListener);
});

//删除自定义事件
$('#remove_custom').click(function() {
    FrameController.removeListener('broadcast', msgEventListener);
});

在线测试地址: http://www.miaoqiyuan.cn/products/frame-controller/ ,核心代码如下:

/**
 * 源码名称:FrameController.js
 * 实现功能:优雅的处理单页多框架(<iframe>)窗口管理同步问题
 * 作者主页:http://www.miaoqiyuan.cn/
 * 联系邮箱:mqycn@126.com
 * 使用说明:http://www.miaoqiyuan.cn/p/framecontroller-js
 * 最新版本:https://gitee.com/mqycn/FrameController.js
 */
(function() {

    if (window.top == window.self) {

        //父窗口
        window.FrameController = (function() {

            var topFrameId = 'parent', //父窗口ID
                data = {
                    events: {}, //事件
                    counter: {}, //事件计数器
                    _count: 1 //窗口ID
                };

            window._data = data;

            /**
             * 获取新的窗口编号
             */
            var getId = function() {
                return 'frame_' + data._count++;
            };

            /**
             * 事件广播
             */
            var broadcast = function(event, value) {

                var _events = data.events,
                    count = 0;

                if (topFrameId === this.frameId) {
                    value = {
                        type: topFrameId,
                        target: window,
                        data: value,
                        frameId: this.frameId
                    };
                }

                for (var _frameId in _events) {
                    if (_frameId != value.frameId && (event in _events[_frameId])) {
                        for (var _funcId in _events[_frameId][event]) {
                            _events[_frameId][event][_funcId](value);
                            count++;
                        }
                    }
                }

                return count;

            };

            /**
             * 添加监听事件
             */
            var addListener = function(event, func) {

                var _events = data.events,
                    _counter = data.counter,
                    _id = this.frameId;

                if (!(_id in _events)) {
                    _events[_id] = {};
                    _counter[_id] = {};
                }

                if (!(event in _events[_id])) {
                    _events[_id][event] = {};
                    _counter[_id][event] = 1;
                }

                _events[_id][event][_counter[_id][event]++] = func;
            };

            /**
             * 删除监听事件
             * 如果不指定func会删除所有本类型的事件
             */
            var removeListener = function(event, func) {
                var _events = data.events,
                    _id = this.frameId;

                if ((_id in _events) && (event in _events[_id])) {
                    var _funcs = _events[_id][event];
                    for (var _funcId in _funcs) {
                        if (_funcs[_funcId] == func || !func) {
                            delete _funcs[_funcId];
                            if (!!func) {
                                //如果指定 func,只会删除一个
                                break;
                            }
                        }
                    }
                }

            };

            /**
             * 删除所有监听事件
             */
            var removeAllListener = function() {
                delete data.events[this.frameId];
                delete data.counter[this.frameId];
            };

            return {
                frameId: topFrameId,
                broadcast: broadcast,
                addListener: addListener,
                removeListener: removeListener,
                removeAllListener: removeAllListener,
                getId: getId
            }
        })();
    } else {

        //子窗口
        window.FrameController = (function() {
            var TopController = window.top.FrameController,
                frameData = {
                    frameId: TopController.getId()
                };

            //广播
            var broadcast = function(event, value) {
                return TopController.broadcast.call(frameData, event, {
                    event: event,
                    type: 'child',
                    target: window,
                    data: value,
                    frameId: frameData.frameId
                });
            };

            //窗口加载或关闭
            var listenerName = 'attachEvent';
            var listenerPrefix = 'on';
            if ('addEventListener' in window) {
                listenerName = 'addEventListener';
                listenerPrefix = '';
            }

            //获取窗口数量
            var getCount = function() {
                return FrameController.broadcast('frame._online') + 1;
            };

            window[listenerName](listenerPrefix + 'load', function() {

                //窗口注册事件
                FrameController.broadcast('frame.add', {
                    msg: '新增窗口'
                });

                //计数事件,仅用于统计框架数
                FrameController.addListener('frame._online', function() {});
            });

            window[listenerName]('unload', function() {

                //窗口关闭事件
                FrameController.broadcast('frame.remove', {
                    msg: '关闭窗口'
                });
                FrameController.removeAllListener();

            });

            //兼容IE8和之前的浏览器
            var bindFrameData = function(func) {
                return function(event, data) {
                    func.call(frameData, event, data);
                };
            }

            return {
                broadcast: broadcast,
                addListener: bindFrameData(TopController.addListener),
                removeListener: bindFrameData(TopController.removeListener),
                removeAllListener: bindFrameData(TopController.removeAllListener),
                count: getCount
            }
        })();
    }

})();

在 父窗口中 直接调用 FrameController.js 即可。对于子窗口(iframe),调用 FrameController.addListener 可以创建 监听事件,FrameController.broadcast 可以对 所有子窗口 进行 广播。比如:

//同步通知
FrameController.addListener('broadcast', function(e) {
    $('#msg').val(e.data.msg);
    console.log(e.frameId, e.event, e.data);
});

//发送广播
$('#send').click(function() {
    var nums = FrameController.broadcast('broadcast', {
        msg: $('#msg').val()
    });
    console.log('通知成功:', nums);
});

通知消息结构:

{
    event: '事件名称',
    type: 'child',
    target: '内嵌页的window',
    data: '传递的数据,即FrameController.broadcast(event, data)的data',
    frameId: '内嵌页标志'
}

以上所述就是小编给大家介绍的《FrameController.js – 优雅的处理单页多框架(iframe)窗口管理同步问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

第三次工业革命

第三次工业革命

[美] 杰里米•里夫金(Jeremy Rifkin) / 张体伟 / 中信出版社 / 2012-5 / 45.00元

第一次工业革命使19世纪的世界发生了翻天覆地的变化 第二次工业革命为20世纪的人们开创了新世界 第三次工业革命同样也将在21世纪从根本上改变人们的生活和工作 在这本书中,作者为我们描绘了一个宏伟的蓝图:数亿计的人们将在自己家里、办公室里、工厂里生产出自己的绿色能源,并在“能源互联网”上与大家分享,这就好像现在我们在网上发布、分享消息一样。能源民主化将从根本上重塑人际关系,它将影响......一起来看看 《第三次工业革命》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码