【问题标题】:Run code in context of frame window在框架窗口的上下文中运行代码
【发布时间】:2018-02-10 02:47:04
【问题描述】:

我想在 iframe 窗口的上下文中运行一些 javascript。现在我能想到的唯一方法是注入一个脚本标签:

myIframe = document.createElement('iframe');
myIframe.setAttribute('name', 'xyz123');
document.body.appendChild(myIframe);

myIframe.contentWindow.document.write(`
    <script>
        console.log('The current window name is:', window.name);
    </script>
`);

注意:这是一个同域 iframe,没有 src,所以我可以完全访问 contentWindow

对于我的用例来说,代码使用正确的全局变量运行很重要; windowdocument 等都应该在 iframe 本身的范围内。

还有其他方法可以做到这一点吗?以上方法可行,但脚本需要在不同的域上运行,所有域都有不同的 CSP 规则,这意味着添加对 nonces/hash 等的支持。

是否可以这样做:

myIframe.contentWindow.run(function() {
    console.log('The current window name is:' window.name);
});

我已经尝试过myIframe.contentWindow.setTimeout,但这似乎仍然在父窗口的上下文中运行代码。

【问题讨论】:

    标签: javascript html browser


    【解决方案1】:

    您实际上可以创建run 函数,然后将回调函数应用于this,这当然是iframe 上下文。然后您可以使用this 访问 iframe 元素:

    myIframe.contentWindow.run = function(fn) {
        fn.apply(this);
    };
    
    myIframe.contentWindow.run(function() {
        console.log('(run) The current window name is:', this.window.name);
    });
    

    控制台输出

    (run) The current window name is: xyz123
    

    您可以在这里查看我的示例:http://zikro.gr/dbg/html/con-frame/

    编辑

    如果您只想使用window 而不是this.window,则可以为内联函数创建一个参数,名称为window,然后将this.window 传递给该函数,如下所示:

    myIframe.contentWindow.run = function(fn) {
        fn.call(this, this.window);
    };
    
    myIframe.contentWindow.run(function(window) {
        console.log('(run) The current window name is:', window.name);
    });
    

    它仍然按预期工作。

    【讨论】:

    • 这是一个很好的例子——唯一的问题是,它改变了 iframe 中存在的任何函数的语义,需要引用this.window 而不仅仅是window——但这是绝对是迄今为止最好的答案。谢谢!
    • @bluepnume 不客气。我更新了我的答案,使其具有一个内联函数,该函数具有一个名称为 window 的参数,您可以使用它;它消除了使用this.window 的需要。除非您需要从特定源显式应用整个内联函数,否则这将起作用,那么就不可能拥有该特定参数。
    【解决方案2】:

    也许将 javascript 拆分为从主窗口(我们称之为 main.js)和 iframe(我们称之为 iframe.js)部分运行。然后在 iframe 的 src 放置链接到 iframe.jsiframe.html 加载 js 文件(我不确定你是否可以直接包含来自 src 属性的 javascript)。

    如果你将js加载到iframe,使用Calling a function inside an iframe from outside the iframe的解决方案。

    【讨论】:

    • 感谢您的建议——但对于我的用例,我需要能够将任意 javascript 插入框架中,该框架不一定托管在任何地方——因此是脚本标签。
    【解决方案3】:
    window.name='main window'; // just for debugging
    var myIframe = document.createElement('iframe'), frameScript = document.createElement('script');
    document.body.appendChild(myIframe);
    frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); "
    myIframe.contentWindow.document.documentElement.appendChild(frameScript);
    function OnMessage(event) {
      if(typeof event.data == 'string') switch(event.data) {
      case 'frame_initialized':
        myIframe.contentWindow.document.body.appendChild( document.createTextNode(myIframe.contentWindow.WhereAmI()) );
        console.log('Now running in', myIframe.contentWindow.WhereAmI());
        break;
      }
    }
    window.addEventListener('message', OnMessage, false);
    

    用 Firefox 和 Chromium 测试。

    您可以将 .src 应用于 frameScript,而不是 .textContent,因此脚本可以异步加载。然后可以如上图调用postMessage或者调用回调函数通知父窗口。

    请注意,在您的原始代码中 window.frameElement.name 已初始化。然后,您的脚本会询问 window.name。 FireFox 会自动将该值复制到窗口中,造成一些混乱。

    【讨论】:

    • 为了改进这个答案,我需要更好地了解您的情况。您可以访问两个域上的 HTML 吗?它应该只为您本地运行还是为所有人运行?是否可以使用像 GreaseMonkey 这样的脚本管理器?
    • 感谢您的建议,但我不确定 window.postMessage 是否会专门帮助我在框架中运行任意代码——我需要一些方法在框架中添加消息侦听器这工作,这让我回到原来的问题。
    • 要回答您的问题,这需要对每个人都有效,因此油脂猴不起作用。没有第二个域——加载 iframe 时没有 src
    【解决方案4】:

    您需要异步加载脚本(即$.getScript()),然后在.contentWindow 上调用它。我还没有测试过,但这应该可以。

    (function() {
    
       var myIframe = document.createElement('iframe#iframe'):
    
       var jsSnippet;
       var iframeScript = function(url) {
            script = document.createElement('script'),
            scripts = document.getElementsByTagName('script')[0];
            script.src = url;
            return jsSnippet = scripts.parentNode.insertBefore(script, scripts);
        };
    
        myIframe.setAttribute('name', 'xyz123'):
        myIframe.appendChild(jsSnippet);
        document.body.appendChild(myIframe);
        return document.getElementById('#iframe').contentWindow.iframeScript();
    })
    

    如果此解决方案不起作用,这两个资源将很有帮助: https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/

    Invoking JavaScript code in an iframe from the parent page

    【讨论】:

      猜你喜欢
      • 2020-11-24
      • 2015-03-01
      • 2012-06-08
      • 1970-01-01
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 2020-09-16
      相关资源
      最近更新 更多