V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
biguokang
V2EX  ›  程序员

一个关于 iframe 很艹蛋的前端需求

  •  
  •   biguokang ·
    AlpacaBi · 301 天前 · 6261 次点击
    这是一个创建于 301 天前的主题,其中的信息可能已经有所发展或是发生改变。

    业务是这样的

    我们用 iframe 调用了 [被调用方] 的页面,然后 [被调用方] 页面一些 a 标签的 target 是“blank”的,这就导致点击他们的 a 标签会打开新的 tab

    而现在的需求是,在 iframe 里的任何操作,都 [ [不允许弹出新 tab 或者新窗口] ] ,如果 [被调用方] 的页面的确是要打开新 tab 的,那就改为在本地页面跳转。

    正常来说是无法实现的,因为跨域,而且 [被调用方] 肯定不会改代码的,所以只能写了个浏览器插件,通过注入代码的形式强行把网页里所有的 a 标签的 target 改为 self,这样 a 标签就全都是本地跳转

    但是 [被调用方] 网页里面的业务不一定全是用 a 标签来打开新 tab,他们可能一些业务是用 js 来打开新 tab 的(类似于 window.open 这种,我尝试在插件里把 window.open 覆写成 window.location.href,但也只是部分按钮实现了,有些还是会跳出新页面)

    现在问题如下: 1.被调用方是肯定不会改代码的,这个无解

    2.已经能通过写浏览器插件的方式来注入代码强改客户代码的 a 标签(因为被调用方是不同域的,而且非互联网项目,装插件的设备也就那么几台,也不麻烦)

    3.不能说这玩意不能做,因为项目跑在超高分大屏(电影院银幕大小)上,网页全屏显示,他有这种需求是因为打开新 tab 会强制关掉网页全屏,导致观感不好

    然后如何完美实现??(虽然以我的见解这玩意是无法实现的,用浏览器插件来搞已经很仁至义尽了)

    51 条回复    2021-02-04 05:59:33 +08:00
    SlipStupig
        1
    SlipStupig   301 天前   ❤️ 3
    父页面往子页面做事件注入,监听点击事件并且 Hook 掉一些事件和函数,在父页面创建一个事件监听器,然后定义一个 receiveMessage 函数,子页面通过 window.postMessage 做跨域通信,父页面就能知道具体的点击事件了,新 tab 可以先请求页面然后覆盖掉原始的 body,这要不用跳转直接刷新了,思路仅供参考哈
    Osk
        2
    Osk   301 天前 via Android
    chrome 的 --kiosk 模式试试?
    tanranran
        3
    tanranran   301 天前
    写个浏览器的壳?
    cheng6563
        4
    cheng6563   301 天前
    服务端也可以搞个[被调用方] 的代理程序,然后注入代码。可能比浏览器插件好用点?
    meepo3927
        5
    meepo3927   301 天前
    用 Electron ? 没实践过,只是提供一个思路。

    或者 nginx 做代理,把 iframe 代理到 [被调用方] 的域上,这样 iframe 就是同域了,不知能否解决。
    yoshiyuki
        6
    yoshiyuki   301 天前
    nginx 或者 nodejs 来代理被调用页面,使用 replace 把 target 替换掉
    biguokang
        7
    biguokang   301 天前
    @SlipStupig 一开始和你想法一样,但是实际项目情况,注入不了,因为子页面是跨域的,contentDocoument 是 null 的,直接操作 contentWindow 直接报跨域错误,所以后来才想着写浏览器插件来注入代码才改的了子页面的逻辑。。。。。而且你想使用 postMessage 来通信,需要改被调用方的代码来接收的,但是实际场景被调用方的代码他们是不能改的。。。。如果可以改被调用方的业务代码,也不会有那么多事了
    zenxds
        8
    zenxds   301 天前
    有些还是会跳出新页面?要具体看下有些是什么情况,比如 document.createElement 创建的 a 标签,需要把 document.createElement 也重写了
    mknightoy
        9
    mknightoy   301 天前
    浏览器关掉跨域保护不就完了么,反正都是展示用的不需要跨域保护
    SlipStupig
        10
    SlipStupig   301 天前
    @biguokang 关闭浏览器的跨域选项或者用 Nginx 做代理,都可以啊
    agee
        11
    agee   301 天前
    自己做个套壳浏览器,就可以自己处理所有打开新窗口事件,这样不需要管网页上的代码了。理论上好像是这样,没具体试过。
    winterx
        12
    winterx   301 天前   ❤️ 2
    难道就只有我想知道为什么被调用方不肯改代码?
    SakuraKuma
        13
    SakuraKuma   301 天前
    支持 nginx 反代,把 target 都干掉
    除非额外有骚操作 js 跳转之类的
    biguokang
        14
    biguokang   301 天前
    @winterx 我也想知道,但是人家就是那样,无解
    stillyu
        15
    stillyu   301 天前
    设置浏览器禁止打开弹出窗口?
    DrakeXiang
        16
    DrakeXiang   301 天前
    有浏览器扩展强制在同一个 tab 打开新页面
    SystemLight
        17
    SystemLight   301 天前
    为什么不把 iframe 中内容读取出来,然后动态修改里面内容 再呈现呢 ?
    msmmbl
        18
    msmmbl   301 天前 via Android
    可以用 nw.js 的话,他有一个 new-win-policy 和 navigation 事件可以控制跳转行为。
    leo108
        19
    leo108   301 天前
    window.open = function(url) { location.href = url }
    window.open('https://v2ex.com')
    ciqulover
        20
    ciqulover   301 天前 via iPhone
    既然都有浏览器插件了 咋还动不了 iframe 跳转逻辑呢?直接 intercept 掉 iframe 内的 script 加载返回体,正则全局替换 window.open,如果内联在 html 里的 script 代码,用 mutation observer 劫持替换就行。
    musi
        21
    musi   301 天前 via iPhone   ❤️ 2
    其实 iframe 还有个 sandbox 属性可以设置 allow-popups

    详情左转 https://developer.mozilla.org/zh-cn/docs/web/html/element/iframe
    Hanada
        22
    Hanada   301 天前
    为什么不考虑一下直接反向代理被调用方的网页,然后就可以根据需要进行改动了
    zqx
        23
    zqx   300 天前 via Android
    避免 iframe 的安全策略,就肯定不能用 iframe 了。
    可以在你的 js 里,把别人的 html 请求回来,解析并插入到自己的 DOM 中,再请求 css js,是 vue 或 react 或原生 js,按对应的逻辑执行。关键难点在于要拿到他的路由和状态管理,如果他没有在代码里显式导出给你,就很难拿到,需要从源码里找
    微前端的实现大概也是这个思路
    sampeng
        24
    sampeng   300 天前 via iPhone
    这你都干…怼回去啊。怎么不让做个屏幕随着衣服颜色变色呢?
    uiosun
        25
    uiosun   300 天前
    iframe 不是可以禁止跳转吗……就在基本配置里啊……

    是我没理解对需求,还是你好歹去看看文档啊……
    zhw2590582
        26
    zhw2590582   300 天前
    Chrome 可以监听新页面打开事件,看能不能在打开的时候马上关闭,并 iframe 重定向
    coderfuns
        27
    coderfuns   300 天前
    iframe sandbox 属性,但是 ie9 前是不支持的。
    GM
        28
    GM   300 天前
    自己写一个程序,内嵌 webview,不要用第三方浏览器,这样有 100%的控制权,随便搞。
    biguokang
        29
    biguokang   300 天前
    @uiosun 老哥,我当然知道 iframe 是可以通过设置 sandbox 来禁止跳转,这个不用你教。。。问题是我的需求不是禁止跳转,而是把打开新窗口的逻辑改为在原地跳转
    biguokang
        30
    biguokang   300 天前
    @coderfuns 这玩意我是有设置的,唯一的作用也就是禁止 iframe 页面打开新窗口而已,只不过满足不了需求,需求是把打开新窗口的操作变为原地跳转
    biguokang
        31
    biguokang   300 天前
    @musi 这玩意我是有设置的,唯一的作用也就是禁止 iframe 页面打开新窗口而已,只不过满足不了需求,需求是把打开新窗口的操作变为原地跳转
    biguokang
        32
    biguokang   300 天前
    @ciqulover 我是自己开发了一个浏览器插件,通过注入的方式解决了,说白了就是通过事件委托检测到点击了 a 标签后,然后禁止调 a 标签的行为,改为 window.location.href=xxx 就实现了。。。但只是解决了 a 标签的跳转逻辑,而 js 的跳转还没有
    shaoyijiong
        33
    shaoyijiong   300 天前
    我们有一个类似的需求 我们是在后端写一个代理服务 , 前端请求那个代理 , 代理请求对应的网页 ; 然后在代理服务里面写入自己的 js 代码 , 就可以修改原来的 html 元素里面的 a 标签了 ; 或者直接修改原始 html 也行
    后端使用的是 java 做的代理 https://github.com/mitre/HTTP-Proxy-Servlet
    shaoyijiong
        34
    shaoyijiong   300 天前
    也可以在嵌入的 js 中取消别人的点击事件
    no1xsyzy
        35
    no1xsyzy   300 天前
    用 Nginx 反代改成同域啊
    Aria2 的老技巧了(
    dlllcs
        36
    dlllcs   300 天前
    electron 可解
    ArthurSS
        37
    ArthurSS   300 天前
    https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/iframe
    sandbox
    该属性对呈现在 iframe 框架中的内容启用一些额外的限制条件。属性值可以为空字符串(这种情况下会启用所有限制),也可以是用空格分隔的一系列指定的字符串。有效的值有:
    ...
    allow-popups: 允许弹窗 (例如 window.open, target="_blank", showModalDialog)。如果没有使用该关键字,相应的功能将自动被禁用。
    ...
    mxT52CRuqR6o5
        38
    mxT52CRuqR6o5   300 天前
    不能改代码,那能不能看代码,遇到没拦截住的就具体看看代码里是咋跳的,就知道为啥没拦截到了
    ArthurSS
        39
    ArthurSS   300 天前
    @ArthurSS #37 刚刚没看到楼上评论。。。
    krapnik
        40
    krapnik   300 天前
    19 楼的方法解决不了嘛?
    CODEWEA
        41
    CODEWEA   300 天前
    我做过类似的,用 postMessage 通信即可
    lixiangzaizheli
        42
    lixiangzaizheli   300 天前
    重写跳转注入不就好了
    ganbuliao
        43
    ganbuliao   300 天前
    解决办法就是 : 劫持注入 js ,插件注入 js,套壳写浏览器,
    biguokang
        44
    biguokang   300 天前
    @CODEWEA postMessage 无解,改不了被调用方的代码
    biguokang
        45
    biguokang   300 天前
    @mxT52CRuqR6o5 被调用方的代码是 webpack 打包过的,变量名也被打包工具魔改了,基本无可读性
    PineappleBeers
        46
    PineappleBeers   300 天前
    我觉得你当前的需求用 4 楼和 19 楼的方法结合就来就可以解决。
    浏览器请求你的服务。
    服务从被调用方的地址获取到页面。
    从获取到的 html 里注入一个 script 标签,标签里的内容就是重写 window.open 。
    然后将注入后的页面返回浏览器。
    biguokang
        47
    biguokang   300 天前
    @PineappleBeers 他们可能没看完我的问题,我问题里说了已经我已经 override 了 window.open,但也只是一部分生效而已。。。而且所有的 a 标签已经被我的注入代码改成 target=_self 了
    zhuweiyou
        48
    zhuweiyou   300 天前
    被调用方的页面 用服务端代理 + 改写
    R18
        49
    R18   300 天前 via Android
    制作专用的浏览器呢?
    autoxbc
        50
    autoxbc   299 天前
    并不需要读懂对方代码,只要把能够进行地址跳转的函数全部覆盖一遍,大概一只手就能数过来
    billccn
        51
    billccn   299 天前
    想到我十年前做的老项目,当时是为一个触摸屏的工业设备做一些辅助功能,公司不想花钱去买厂家的 SDK,就要求利用内置的浏览器实现,最骚的就是偶尔要内嵌显示另一个网站,要可以交互,但是不能允许该网站弹窗或者影响宿主页面,这些是 iframe 做不到的(有这些功能那机子的内存也不够)。

    最后发现那个内置的浏览器可以支持 Java applets,就直接在网页里套了个 Java 版的 VNC 客户端,连接到服务器上一个无壳的 Chrome,弹窗什么的都只在服务器上进行,怎么也不会影响到客户端,而且客户端可以看到所有的效果。现在时代进步了,VNC 客户端可以直接用 JS 实现了。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2103 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 16:11 · PVG 00:11 · LAX 08:11 · JFK 11:11
    ♥ Do have faith in what you're doing.