Iframe页面间通信详解

1,552 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

iframe常用属性

使用 iframe 通常直接在页面嵌套 iframe 指定 src 即可

<iframe src="demo_iframe_sandbox.htm"></iframe>

iframe 常用属性:

  1. frameborder:是否显示边框,1(yes)0(no)
  2. height:框架作为一个普通元素的高度,建议在使用 css 设置。
  3. width:框架作为一个普通元素的宽度,建议使用 css 设置。
  4. name:框架的名称,window.frames[name]时专用的属性。
  5. scrolling:框架的是否滚动。yesnoauto
  6. src:内框架的地址,可以使页面地址,也可以是图片的地址。
  7. srcdoc : 用来替代原来 HTML body 里面的内容。但是 IE 不支持, 不过也没什么卵用
  8. sandbox:对 iframe 进行一些列限制,IE10+支持

获取 iframe 内容

iframe.contentWindow

获取 iframewindow 对象

document.getElementById("iframe").conetntWindow;

iframe.contentDocument

获取 iframedocument 对象

document.getElementById("iframe").contentDocument;

name 属性获取 window 对象

<iframe src="/index.html" id="ifr1" name="ifr1" scrolling="yes">
  <p>Your browser does not support iframes.</p>
</iframe>
<script type="text/javascript">
  console.log(window.frames["ifr1"].window); // 与下面方式获取等价
  console.dir(document.getElementById("ifr1").contentWindow);
</script>

发送消息

对于嵌套iframe的跨域传递消息,一般都采用 postMessage 方法。该方法挂载到目标页面的 window 对象上,即,使用 window.postmessage()调用。

targetWindow.postMessage(message, targetOrigin)

该方法接受两个参数:

  • message:字符串,不要传 Object 对象,如果要传可以先使用 JSON.stringify 转成字符串
  • targetOrigin:接受你传递消息的域名,可以设置绝对路径,也可以设置*或者/*表示任意域名都行,/表示只能传递给同域域名。

接收信息

window.addEventListener('message',fn(event){})

targetOrigin 接受到 message 消息之后,会触发 message 事件。 message 提供的 event对象上有 3 个重要的属性,data,origin,source

  • datapostMessage 传递进来的值
  • origin:发送消息的文档所在的域
  • source:发送消息文档的 window 对象的代理,如果是来自同一个域,则该对象就是 window,可以使用其所有方法,如果是不同的域,则 window 只能调用 postMessage()方法返回信息

实例

现在有这样一个需求:在一个使用 iframe 多层嵌套的网页最里层中(具体会嵌套多少层不确定,这里只模拟了四级嵌套),创建一个充值按钮点击事件,点击按钮就在第二层页面的 body 节点下新建一个 iframe 用于展示充值页面

要求:

  • 必须要在第二层嵌套页面创建 iframe,其他任何嵌套页面创建都不可以;
  • 只创建或删除 iframe 节点,不要操作页面其他任何内容;
  • 新创建的 iframe 节点必须全屏展示,层级要为最高;
  • 新创建的 iframe 节点要能够通过关闭按钮把自己销毁;

要点:

  • 多层嵌套的 window 对象定位问题
  • iframe 之间的通信问题

第一层页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>h5Iframe</title>
    <style>
      html,
      body {
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <iframe
      src="./2.gameIframe.html"
      id="h5Frame"
      frameborder="0"
      width="100%"
      height="100%"
    ></iframe>

    <script>
      window.addEventListener("message", function(event) {
        console.log(1);
        console.log(event);
      });
    </script>
  </body>
</html>

第二层页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>h5Iframe</title>
    <style>
      html,
      body {
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
      }
    </style>
  </head>

  <body>
    <iframe
      id="gameFrame"
      src="./3.iframe.html"
      frameborder="0"
      width="100%"
      height="100%"
    ></iframe>

    <script>
      function $(selector) {
        return document.querySelector(selector);
      }

      // 当监听到data=open时,就创建一个充值的iframe
      window.addEventListener("message", function(event) {
        console.log(2);

        if (event.data == "open") {
          let iframe = document.createElement("iframe");
          iframe.id = "payIframe";
          iframe.src = "./index.html";
          iframe.setAttribute("frameborder", "0", 0);
          iframe.scrolling = "no";
          iframe.style.width = "100%";
          iframe.style.height = "100%";
          iframe.style.position = "fixed";
          iframe.style.top = "0";
          iframe.style.left = "0";
          iframe.style.zIndex = "1000000";
          $("body").appendChild(iframe);
        } else if (event.data == "close") {
          $("#payIframe").remove();
        }
      });
    </script>
  </body>
</html>

第三层页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>h5Iframe</title>
    <style>
      html,
      body {
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <iframe
      src="./4.page.html"
      id="iframe"
      frameborder="0"
      width="100%"
      height="100%"
    ></iframe>

    <script>
      window.addEventListener("message", function(event) {
        console.log(3);
        console.log(event);
      });
    </script>
  </body>
</html>

第四层页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>h5Iframe</title>
    <style>
      html,
      body {
        padding: 0;
        margin: 0;
        width: 100%;
        height: 100%;
      }
    </style>
  </head>
  <body>
    <button id="btn">充值</button>

    <script>
      function $(selector) {
        return document.querySelector(selector);
      }

      $("#btn").addEventListener("click", function() {
        top.document
          .getElementById("h5Frame")
          .contentWindow.postMessage("open", "*");

        // top.frames['h5Frame'].window.postMessage('open', '*');     与上面方式等价,这是通过name获取节点的window对象
      });

      // console.log(top.document.getElementById('h5Frame').contentWindow)
    </script>
  </body>
</html>

充值页面

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>选择金额</title>
    <style>
      html,
      body {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
        background: #f5f5f5;
        overflow-y: scroll;
        display: flex;
        flex-direction: column;
      }
      .nav {
        width: 100%;
        height: 50px;
        line-height: 50px;
        text-align: center;
        font-size: 18px;
        font-weight: 400;
        border-bottom: 2px solid #ddd;
        position: relative;
      }
      .nav .close {
        width: 50px;
        height: 50px;
        position: absolute;
        right: 0;
        top: 0;
        color: #aaa;
      }

      .main {
        flex: 1;
      }
      .main .list {
        width: 90%;
        padding: 10px;
        background: #fff;
        border-radius: 10px;
        margin: 16px auto;
        box-sizing: border-box;
      }
      .main .list .li {
        width: 100%;
        height: 40px;
        line-height: 40px;
        border-bottom: 1px solid #f5f5f5;
        display: flex;
        justify-content: space-between;
        padding: 0 10px;
        box-sizing: border-box;
      }
      .main .list .li .li_detail {
        color: #aaa;
        font-size: 14px;
      }
      .main .list .li:last-child {
        border-bottom: 0;
      }

      .main .btn {
        display: block;
        width: 80%;
        background: #ff8500;
        margin: 30px auto;
        text-align: center;
        border: none;
        padding: 10px 0;
        color: #fff;
        border-radius: 6px;
        outline: 0;
        font-size: 16px;
      }
    </style>
  </head>
  <body>
    <div class="nav">
      <div class="title">充值中心</div>
      <div id="close" class="close">X</div>
    </div>
    <div class="main">
      <!-- 信息列表 -->
      <div class="list">
        <div class="li">
          <div class="li_name">购买道具</div>
          <div class="li_detail">6000元宝</div>
        </div>
        <div class="li">
          <div class="li_name">道具原价</div>
          <div class="li_detail">6.0元</div>
        </div>
        <div class="li">
          <div class="li_name">抵扣券</div>
          <div class="li_detail">选择抵扣券</div>
        </div>
        <div class="li">
          <div class="li_name">应付金额</div>
          <div class="li_detail">6.0元</div>
        </div>
      </div>

      <!-- 按钮 -->
      <button class="btn">确认支付</button>
    </div>

    <script>
      function $(selector) {
        return document.querySelector(selector);
      }

      $("#close").addEventListener("click", function() {
        top.document
          .getElementById("h5Frame")
          .contentWindow.postMessage("close", "*");
      });
    </script>
  </body>
</html>