Javascript 与 Iframe Iframe 我们为什么真的需要 Javascript?问题所在(同源策略) 发送消息 API 接收 Iframe 事件中的消息

2025-06-10

Javascript 和 Iframe

内联框架

我们为什么真的需要 Javascript?

问题(同源策略)

发送消息 API

在 iframe 中接收消息

活动

从标题你就知道我要写关于Javascriptiframe 的文章了。在日常生活中,作为一名开发者或博主,在网站中嵌入 iframe 非常简单。

例如,如果您需要将 YouTube 视频嵌入到您的网站,您只需从 YouTube 复制代码并将其粘贴到您的网站即可。



<iframe width="560" height="315" src="https://www.youtube.com/embed/$SOME_ID" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>


Enter fullscreen mode Exit fullscreen mode

这就是我所说的“直截了​​当”。

我意识到这一点,并在几个月前开始开发一个评论插件Hyvor Talk 。这并不容易。在本文中,我将解释其中的原因。在创建这个插件的过程中,我学到了很多东西,也设计了一些技巧。我将在本文中解释这些技巧。如果您计划开发一个插件或任何使用 iframe 加载到其他网站的东西(例如评论插件),本文将帮助您了解如何有条理地完成某些任务。此外,我还会解释如何克服您在开发此类插件时可能遇到的障碍。

在本教程中,我们假设您正在创建一个插件,该插件会加载到其他网站的 iframe 中(我将该网站的所有者称为“客户”)。此外,您的网站域名为pluginx.com,而客户网站的域名为client.com

内联框架

首先,让我们了解一下什么是 Iframe。

HTML 内联框架元素 () 表示嵌套的浏览上下文,将另一个 HTML 页面嵌入到当前页面中 - MDN

在本文中,我不仅仅会向网站添加 HTML 代码,还会重点介绍如何使用 Javascript 处理 Iframe。

我们为什么真的需要 Javascript?

JavaScript 可以让你更好地控制 iframe。尤其是,当 iframe 中的内容发生变化时自动调整 iframe 的大小,这只能通过 JavaScript 来实现。这需要 iframe(你的网站)和网站(其他网站)之间的通信。所以,最好的方法不是给<iframe>客户端提供代码,而是让他们添加一个 JavaScript 文件。

客户将代码添加到他的网站。代码包括一个<script>标签和一个<div>标签(iframe容器)。



<html>
<head>
   ...
</head>
<body>
   ...
   <div id="pluginx-iframe-container"></div>
   <script src="https://pluginx.com/iframe-loader.js"></script>
</body>


Enter fullscreen mode Exit fullscreen mode

现在,我们可以编写iframe-loader.js来加载托管在您的域上的 HTML 页面。

添加 iframe

此任务需要简单的 JavaScript 代码。需要记住的一点是,将所有变量都放在局部作用域中。这可以通过将代码包装在自调用函数中来实现。这可以防止你的 JS 变量与客户端网站的 JS 变量发生冲突。如果你使用像webpack这样的打包工具,则无需这样做,因为 webpack 会将所有变量都设置为局部变量。



(function() {

   var iframe = document.createElement("iframe");
   iframe.src = "https://pluginx.com/iframe"; // iframe content (HTML page)
   iframe.name = "pluginx-main-iframe"; // this is important

})(); 


Enter fullscreen mode Exit fullscreen mode

我稍后会解释该属性的重要性name。请确保为其设置一个唯一的名称。

我发现添加以下属性可以使您的 iframe 在所有浏览器中看起来都很好。



iframe.width = "100%";
iframe.allowTranparency = true;
iframe.tabIndex = 0;
iframe.scrolling = "no";
iframe.frameBorder = 0;

iframe.style.border = "none";
iframe.style.overflow = "hidden";
iframe.style.userSelect = "none";
iframe.style.height = "250px"; // set initial height


Enter fullscreen mode Exit fullscreen mode

现在,我们可以附加<iframe>



var container = document.getElementById("pluginx-iframe-container");
container.appendChild(iframe);


Enter fullscreen mode Exit fullscreen mode

问题(同源策略)

如果两个网站都在同一个域名下,那么你就可以简单地执行彼此页面的功能、访问变量等等。但是,根据同源策略,这是无法做到的。如果两个网站不在同一个域名下,浏览器会限制父网站访问 iframe 窗口的功能和变量,反之亦然。起初,我认为这是不必要的安全措施。但现在我明白了,这是保护网站安全的最佳方法之一。为什么?因为如果你的客户端可以使用 JavaScript 访问你的网站,他们就可以运行你的功能、更新内容、点击按钮、添加点赞、窃取用户数据等等。

几种方法可以绕过同源策略。最好的方法是使用HTML 5 Post Message API

发送消息 API

在完成此应用程序的开发之前,不要考虑Post Message API以外的任何其他好朋友!

window.postMessage()方法安全地启用了 Window 对象之间的跨域通信。我们可以使用它来实现插件和客户端网站之间的通信。我们可以通过该window.onmessage事件监听 postMessages。请注意,您只能通过此事件发送字符串。

在创建 Hyvor Talk 时,我创建了一个简单的类来封装这种通信。我推荐你使用它,因为它可以简化流程。代码如下。接下来我会解释一下。



/**
 * Creates a messenger between two windows
 *  which have two different domains
 */
class CrossMessenger {

    /**
     * 
     * @param {object} otherWindow - window object of the other
     * @param {string} targetDomain - domain of the other window
     * @param {object} eventHandlers - all the event names and handlers
     */
    constructor(otherWindow, targetDomain, eventHandlers = {}) {
        this.otherWindow = otherWindow;
        this.targetDomain = targetDomain;
        this.eventHandlers = eventHandlers;

        window.addEventListener("message", (e) => this.receive.call(this, e));
    }

    post(event, data) {

        try {
            // data obj should have event name
            var json = JSON.stringify({
                event,
                data
            });
            this.otherWindow.postMessage(json, this.targetDomain);

        } catch (e) {}
    }

    receive(e) {
        var json;
        try {
            json = JSON.parse(e.data ? e.data : "{}");
        } catch (e) {
            return;
        }
        var eventName = json.event,
            data = json.data;

        if (e.origin !== this.targetDomain)
            return;

        if (typeof this.eventHandlers[eventName] === "function") 
            this.eventHandlers[eventName](data);
    }

}


Enter fullscreen mode Exit fullscreen mode

我使用 ES6 编写了这个类。如果你没有使用 ES6,你可以使用Babel将其转换为浏览器支持的 JavaScript 。

在解释该类之前,让我们在我们的脚本(iframe-loader.js)中使用它。




// handle each event from the iframe
var clientEventsHandler = {
    resize: (data) => {
        // resize the iframe
    }
};

messenger
var clientMsger = new CrossMessenger(iframe.contentWindow, "https://pluginx.com", eventHandlers)


Enter fullscreen mode Exit fullscreen mode

在向 iframe 发送消息的同时,我们还需要接收消息并对数据做出反应。该clientEventsHandler对象将包含这些函数。(键名和事件名称)。

我们回班级吧。

构造函数需要三个参数。

  • 第一个是我们调用该函数的 iframe 窗口postMessage()
  • 然后,目标域名就是您的网站域名。这使我们能够使用以下方式验证传入的消息e.origin
  • 最后是事件处理程序。稍后我将向您展示如何编写一些常见的事件处理程序,例如调整大小事件。

现在,我们可以使用 向 iframe 发送消息clientMsger.post(eventName, data)。这里的 data 应该是一个对象。它将被转换为 JSON 并发送到 iframe。

在 iframe 中接收消息

目前,我们处理了添加到客户网站的 JavaScript 文件。现在,我们将开始处理 iframe 的脚本。我们需要在 iframe 中使用相同的类来接收消息。

您的 iframe 内容 (HTML)



<html>
  ... html stuffs

  <script src="script.js"></script>
</html>


Enter fullscreen mode Exit fullscreen mode

script.js



var iframeEventHandlers = {}; // some event handlers as the clientEventHandlers
var clientDomain = ""; // How to find this???

var iframeMsger = new CrossMessenger(window.parent, clientDomain, iframeEventHandlers)


Enter fullscreen mode Exit fullscreen mode

我们需要找到客户端的域名。我的做法是在请求 iframe 时设置 GET 参数。



var domain = location.protocol + "//" + location.hostname;  
iframe.src = "https://pluginx.com/iframe?domain=" + domain;


Enter fullscreen mode Exit fullscreen mode

然后,您可以从后端语言接收它并clientDomain动态设置变量。

例如:对于 PHP



var clientDomain = "<?= $_GET['domain'] ?>"; // make sure this is in the PHP page ;)


Enter fullscreen mode Exit fullscreen mode

现在,我们的两个信使都完成了。让我们考虑一下你可能需要的一些事件。

活动

1. 调整大小

默认情况下,iframe 不会自动调整大小。我们必须明确设置 iframe 的高度。否则,iframe 的一部分将无法显示。因此,调整大小是任何 iframe 的关键部分。

调整大小比我们想象的要复杂得多。你需要了解scrollHeightiframe 文档的内容才能调整 iframe 的大小。这只能由 iframe 来实现。

以下是调整 iframe 大小的重要时刻。

  1. 当 iframe 加载时
  2. 当客户端浏览器调整大小时
  3. 当 iframe 内容的高度发生变化时(例如:添加了新元素)

1 号和 2 号(加载和调整大小时)

我们可以从客户端网站上的脚本(iframe-loader.js)监听iframe的onload事件和浏览器的事件。onresize



(function() {

   // setting iframe and appending
   iframe.onload = function() {
       requestResize();
       // do other onload tasks
   }
   window.addEventListener("resize", requestHeight);
})();


Enter fullscreen mode Exit fullscreen mode

函数requestHeight()

这个函数只做一件事:从 iframe 中请求 iframe 的高度。



clientMsger.post("iframeHeightRequest");


Enter fullscreen mode Exit fullscreen mode

这会向 iframe 发送一条消息,事件名称为“iframeResizeRequest”,但没有数据。

我们必须在 iframe 中监听此事件。在iframeEventHandlers中添加事件名称和处理程序script.js



var iframeEventHandlers = {
    iframeHeightRequest: () => {
        var docHeight = document.body.scrollHeight;
        iframeMsger.post("resize", { height: docHeight });
    }
}


Enter fullscreen mode Exit fullscreen mode

现在,我们可以从客户端的脚本中接收高度并调整 iframe 的大小。



var clientEventHandlers = {
    resize: () => {
        var height = data.height;

        var winScrollLeft = window.scrollX,
            windowScrollTop = window.scrollY;

            commentsIframe.style.visibility = "hidden";
            commentsIframe.style.height = 0;

            commentsIframe.style.visibility = "visible";
            commentsIframe.height = height + "px"; // + 10 to add little more height if needed

            // scroll to initial position
            window.scrollTo(winScrollLeft, windowScrollTop);
    }
}


Enter fullscreen mode Exit fullscreen mode

这个过程可能会让人感到困惑。我一开始也有同样的感觉。不过,你很快就能适应。如果你感觉“茫然”,可以稍事休息一下。(我记得我就是这么做的)。休息一会儿后,看看这张照片。

调整图像大小

3.(iframe 高度改变)

在 iframe 中工作时,可能会添加新元素。这会导致 iframe 的高度发生变化。因此,每次高度发生变化时,我们都必须调整 iframe 的大小。

我们需要监听 iframe 中的 DOM 元素变化。这是我从网上找到的一个非常适合这个任务的函数。



var DOMObserver = (function(){
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;

    return function( obj, callback ){
        if( !obj || !obj.nodeType === 1 ) return; // validation

        if( MutationObserver ){
            // define a new observer
            var obs = new MutationObserver(function(mutations, observer){
                callback(mutations);
            })
            // have the observer observe foo for changes in children
            obs.observe( obj, { childList:true, subtree:true });
        }

        else if( window.addEventListener ){
            obj.addEventListener('DOMNodeInserted', callback, false);
            obj.addEventListener('DOMNodeRemoved', callback, false);
        }
    }
})();


Enter fullscreen mode Exit fullscreen mode

将其添加到script.js然后调用该函数。



DOMObserver(document.body, iframeEventHandlers.iframeHeightRequest);


Enter fullscreen mode Exit fullscreen mode

每次添加或删除节点时,iframeEventHandlers.iframeHeightRequest都会调用该函数。iframe 的大小将被调整!

除了调整大小之外,您还可以添加任何事件并在 iframe 和客户端之间传达消息。

如果您正在创建插件或任何在 iframe 内加载的内容,这里有我的一些提示。

  • 将所有数据保留在您的窗口中。仅与客户网站共享必要的数据。切勿共享用户数据等敏感数据。
  • 在您的 iframe 中执行所有 AJAX 操作。
  • 切勿使用CORS并允许他人访问您的网站。请始终使用 postMessage API。

希望本文能帮助您了解如何在 iframe 中使用 JavaScript。我尽力讲解了我在创建 Hyvor Talk 时学到的知识。如有遗漏,我会在以后补充。如果您喜欢这篇文章,请分享并评论。

谢谢。 :)

这篇文章最初发布于Hyvor Groups 的Web Developers Group

鏂囩珷鏉ユ簮锛�https://dev.to/supunkavinda/javascript-and-iframes-87
PREV
7 个用于构建漂亮界面的 React UI 组件库
NEXT
如何拥有一个很棒的 GitHub 个人资料?功能快速入门关于排名 Spotify 最近播放的自述文件 GitHub 个人资料浏览计数器博客文章工作流程自述文件笑话 Github 自述文件引述自述文件中的开发指标,并添加了功能标志🎌 很棒的 GitHub 个人资料自述文件注意:此存储库不包含在 Hacktoberfest 活动中,因为它仅用于练习!