javascript - 用户脚本的两个实例如何在框架之间进行通信?

标签 javascript iframe synchronization cross-domain userscripts

引用在网页和 iframe 中运行相同的 JavaScript 的技术,如 described in this answer :

For example, suppose you have this page at domain_A.com:

<html>
<body>
    <iframe src="http://domain_B.com/SomePage.htm"></iframe>
</body>
</html>

If you set your @match directives like this:

// @match http://domain_A.com/*
// @match http://domain_B.com/*

Then your script will run twice -- once on the main page and once on the iframe as though it were a standalone page.


有哪些选项可以让脚本的两个实例相互通信?

这将需要同步实例。例如,让 iframe 脚本实例仅在网页脚本实例完成后执行其任务,反之亦然。

最佳答案

这两个脚本实例可以使用 postMessage() 相互通信.关于:

This would be needed to sync the instances, so for example to have the iframe one to execute its task only after the webpage one completed, and vice versa.

这就是 this other answer 中显示的内容.
但是 Chrome has bugs in how it presents frames/iframes to extensions 因此,要解决这些错误,您必须注入(inject)调用 postMessage() 的代码。

下面的脚本展示了如何。它:

  • 在 iframe 和包含页面中运行。
  • 处理跨域 iframe。
  • 它演示了具有以下逻辑的脚本间控制:
    1. 容器页面设置为监听来自 iframe 的消息。
    2. iframe 设置为监听来自容器页面的消息。
    3. iframe 将第一条消息发送到容器页面。
    4. 当容器页面收到该消息时,它会将另一条消息发送回 iframe。

安装这个脚本(感谢 CertainPerformance 的更新,针对多年来目标站点的变化):

// ==UserScript==
// @name        _Cross iframe, cross-domain, interscript communication
// @include     http://fiddle.jshell.net/2nmfk5qs/*
// @include     http://puppylinux.com/
// @grant       none
// ==/UserScript==
/* eslint-disable no-multi-spaces */

if (window.top === window.self) return;
console.log ("Script start...");
if (window.location.href.includes('fiddle')) {
    console.log ("Userscript is in the MAIN page.");

    //--- Setup to process messages from the GM instance running on the iFrame:
    window.addEventListener ("message", receiveMessageFromFrame, false);
    console.log ("Waiting for Message 1, from iframe...");
}
else {
    console.log ("Userscript is in the FRAMED page.");

    //--- Double-check that this iframe is on the expected domain:
    if (/puppylinux\.com/i.test (location.host) ) {
        window.addEventListener ("message", receiveMessageFromContainer, false);

        //--- Send the first message to the containing page.
        sendMessageFromAnIframe (
            "***Message 1, from iframe***", "http://fiddle.jshell.net"
        );
        console.log ("Waiting for Message 2, from containing page...");
    }
}

function receiveMessageFromFrame (event) {
    if (event.origin != "http://puppylinux.com")    return;

    console.log ('The container page received the message, "' + event.data + '".');

    //--- Send message 2, back to the iframe.
    sendMessageToAnIframe (
        "#testIframe",
        "***Message 2, from the container page***",
        "http://puppylinux.com"
    );
}

function receiveMessageFromContainer (event) {
    if (event.origin != "http://fiddle.jshell.net")    return;

    console.log ('The iframe received the message, "' + event.data + '".');
}

/*--- Because of bugs in how Chrome presents frames to extensions, we must inject
    the messaging code. See bug 20773 and others.
    frames, top, self.parent, contentWindow, etc. are all improperly undefined
    when we need them.  See Firefox and other browsers for the correct behavior.
*/
function sendMessageFromAnIframe (message, targetDomain) {
    var scriptNode          = document.createElement ('script');
    scriptNode.textContent  = 'parent.postMessage ("' + message
                            + '", "' + targetDomain + '");'
                            ;
    document.body.appendChild (scriptNode);
}

function sendMessageToAnIframe (cssSelector, message, targetDomain) {
    function findIframeAndMessageIt (cssSelector, message, targetDomain) {
        var targetIframe    = document.querySelector (cssSelector)
        if (targetIframe) {
            targetIframe.contentWindow.postMessage (message, targetDomain);
        }
    }
    var scriptNode          = document.createElement ('script');
    scriptNode.textContent  = findIframeAndMessageIt.toString ()
                            + 'findIframeAndMessageIt ("' + cssSelector
                            + '", "' + message
                            + '", "' + targetDomain + '");'
                            ;
    document.body.appendChild (scriptNode);
}

console.log ("Script end");


然后访问this test page at jsFiddle .

您将在 javascript 控制台中看到:

Script start...
Userscript is in the MAIN page.
Waiting for Message 1, from iframe...
Script end
Script start...
Userscript is in the FRAMED page.
Waiting for Message 2, from containing page...
Script end
The container page received the message, "***Message 1, from iframe***".
The iframe received the message, "***Message 2, from the container page***".

关于javascript - 用户脚本的两个实例如何在框架之间进行通信?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11769066/

相关文章:

javascript - 警报后转到iframe中的图像链接

java - 无循环死锁

javascript - 如何给自动生成的元素添加onclick事件

javascript - ASP.NET MVC - JQuery 日期选择器格式

javascript - 如何在 HTML Canvas 中制作阴影

javascript - 两个按钮,用 Jquery 做两件不同的事情

javascript - iframe-resizer:无法禁用自动调整大小

javascript - 如何使用父页面 css 为 iframed 页面内容提供样式?

c - 更好的基准同步

linux - BitTorrent Sync 在重启时重置设备名称