iframe 弹窗取消按钮触发父页面弹窗接口

iframe 弹窗取消按钮触发父页面新增弹窗接口

在开发中,我们可能会因为一些需要需要调用弹窗,进而需要进行父子组件间的通信,但iframe弹窗的新建和销毁又必然会伴随着很多其他的问题,而我最近也遇到了一个关于弹窗销毁带来的一些问题

bug复现

我们的业务需求是在父页面新增一个方法打开一个子页面弹窗,在子页面窗口进行一系列操作然后关闭弹窗,其中我们在关闭子页面弹窗的时候,后台服务器会接收到前端父页面发来的新增弹窗的接口请求,现在我们需要解决这一无端请求的发送问题

父页面打开弹窗的方法

javascript 复制代码
    //回调方法  子页面可以直接对父页面进行操作的回调函数
    window.parent.callback = function (mes) {
        $('#bootstrap-tree-table').bootstrapTable('refresh');
        var layer = layui.layer;
        var index = layer.load(2, {time: 10 * 1000}); //又换了种风格,并且设定最长等待10秒
        setTimeout(function () {
            layer.msg(mes, {icon: 1});
            epoPage.loadData();
            workNum();
        }, 1000)
    };


$(".staffAdd").click(function () {
        window.parent.layer.open({
            type: 2,
            title: false,
			shade:0.2, /*增加遮罩,阻断点击穿透*/
            area: ['55%', '70%'],
            content: '/page/staffAdd'
        });
    })

子页面弹窗以及关闭弹窗

HTML层

html 复制代码
<form class="layui-form" action="" autocomplete="off" id="form">
        <div class="staff-add-action">
            <button class="layui-btn layui-btn-lg save" onclick="sub()" type="button">保存</button>
            <button class="layui-btn layui-btn-primary layui-btn-lg cancel">取消</button>
        </div>
    </form>

JavaScript层

javascript 复制代码
<script th:inline="javascript">
    sub = function () {
            $.ajax({
                url: "/staff/add",
                type: "post",
                dataType: "json",
                data: data,
                success: function (res) {
                    layer.close(loading);
                    if (res.success) {
                        var index = parent.layer.index;
                        parent.callback(res.msg); //调用父页面的回调函数
                        var temp = window.parent.layer.close(index);
                    } else {
                        layer.msg(res.msg);
                    }
                }
            })
        }
        $(".save").attr("readonly", false);
    }

    $(".cancel").click(function () {
        var index = parent.layer.index;
        parent.layer.close(index);
    })
</script>

在这个代码运行的页面当我们点击取消按钮,则父页面会向后端传递 /page/staffAdd 这个打开页面弹窗的请求

解决方案

bug类别

大分类上属于前端 DOM 事件类 Bug

细分属于事件逃逸(Event leakage /event escape) + HTML 表单默认行为陷阱 + iframe 上下文销毁副作用

行业口语叫法:

iframe 销毁事件逃逸 bug(layer iframe 高频坑)

form 内 button 缺少type="button"默认 submit 踩坑

同步销毁 DOM / iframe 导致未完成 DOM 事件异常冒泡逃逸

css 复制代码
注意:这个不是 JS 代码逻辑错误,是HTML 原生默认行为 + 浏览器 DOM 生命周期机制共同叠加出来的复合坑,不是框架 bug,属于开发者对浏览器默认行为理解不足引入的缺陷。

原因拆解

less 复制代码
1.HTML 原生缺陷条件:<form>内的<button>省略 type 属性,浏览器默认 type="submit"。点击按钮会原生触发表单 submit 事件,这是 HTML 标准规定,很多开发者不知道这个默认值。

注:很多人误以为 button 默认是普通按钮,实际 form 标签内部 button 默认是提交按钮。

2.时序缺陷条件:在 click 事件回调里面同步直接执行parent.layer.close(index)立刻销毁 iframe;此时浏览器 DOM 事件冒泡、事件处理流程还没有执行完毕,iframe DOM、JS 上下文直接被浏览器回收。

3.浏览器机制副作用条件:iframe 被销毁后,还在事件队列中未处理完的原生 DOM 事件失去宿主上下文,事件对象 target 错乱,事件向上逃逸到顶层父页面 window;父页面用$(document).on()全局事件委托,匹配到选择器,错误执行父页面回调

总结:三个条件叠加,才出现:点子页面取消,结果执行父页面打开弹窗的逻辑。仅仅缺 type 只会发生子页面表单提交;如果不立刻同步销毁 iframe,事件不会逃逸到父页面。

解决方案

HTML规范

form 表单内部,所有非提交按钮强制写 type 属性

bash 复制代码
提交按钮:<button type="submit">保存</button>

普通按钮 / 取消 / 重置:<button type="button">取消</button>
禁止 form 内部出现不带 type 的裸 button。
html 复制代码
<!-- form表单规范示例 -->
<form class="layui-form" action="" autocomplete="off" id="form">
        <div class="staff-add-action">
            <button class="layui-btn layui-btn-lg save" onclick="sub()" type="button">保存</button>
            <button class="layui-btn layui-btn-primary layui-btn-lg cancel" type="button">取消</button>
        </div>
    </form>

JS 时序规范

bash 复制代码
不要在 DOM 事件回调中同步销毁当前事件所在的 DOM/iframe。
把销毁 / 移除操作丢到下一轮事件循环,使用setTimeout(fn, 0~30),让浏览器把当前 click/submit 事件完整走完,再销毁 DOM。
javascript 复制代码
// 错误:同步销毁,事件还没跑完
parent.layer.close(index);
// 正确:等事件循环完成后再销毁
setTimeout(()=> parent.layer.close(index), 20);
javascript 复制代码
// 取消按钮事件,延时关闭iframe
$(".cancel-btn").on("click", function (e) {
    e.preventDefault();
    let index = parent.layer.getFrameIndex(window.name);
    // 延时执行关闭,避免事件逃逸
    setTimeout(() => {
        parent.layer.close(index);
    }, 20);
});
相关推荐
被摘下的星星1 小时前
CSS 基础说明
前端·css
To_OC11 小时前
从一头雾水到跑通全流程:我用一个周末啃透了JWT登录鉴权
前端·后端·http
excel13 小时前
研究 Vue 3 源码的收获
前端·vue.js
可乐鸡翅yeah_15 小时前
业务中 M3U8 水印相关坑,硬水印和动态水印区别
前端·网络·数据库·ffmpeg·m3u8在线
lerhxx15 小时前
AI 应用如何高效优雅地恢复中断?—— "连接解耦 + 状态持久化"
前端·javascript
计算机魔术师15 小时前
METR 演示 AI 智能体如何篡改 Inspect 评估记录以掩盖不当行为
前端
前端snow15 小时前
ai agent --- 异步处理之 Rabbit MQ
前端
念何架构之路15 小时前
zap扩展生态与总结
java·前端·数据库
独孤九剑打醒他16 小时前
【原创开源】【概念设计】源 - 栅 - 漏 - 栅 - 源 横向双栅 MOS,低压交流多值逻辑芯片探索
前端·其他·架构·开源·硬件工程
mantou13216 小时前
我给 AI Agent 做了个「油猴」:让 Claude Code / Codex 直接用你已登录的浏览器
前端·javascript·后端