如何利用 iframe 扩展我们的项目?

前言

        在初涉HTML领域时,我便首次接触了<iframe>标签,当时仅了解到它能够用于嵌入其他网页内容。然而,在参与实际项目开发过程中,我很少有机会使用这一标签。最近,一个特殊的项目需求使我意识到<iframe>的强大潜力,它能够通过插件的形式为我们的项目带来扩展性。这一发现激发了我的兴趣,因此,我将在这篇文章中详细介绍如何利用<iframe>标签来增强和扩展我们的项目功能。

ifrmae 通信

        单纯地将一个网站嵌入到我们的项目中,这通常不足以应对大多数复杂场景的需求。在绝大多数情况下,我们需要解决的是跨域通信的问题。只有建立了有效的通信机制,我们才能实现数据的交换和共享。通过数据交换,我们能够实现自定义的业务逻辑,从而满足各种场景下的需求,确保项目的灵活性和扩展性。

        为了深入理解<iframe>的通信机制,我们首先准备两个HTML文件:iframe.htmlindex.html。在index.html中,我们将嵌入iframe.html。以下是我们将要实现的需求:

  1. index.html中输入文本后,能够将这段文本传递到iframe.html并显示出来。
  2. 当在iframe.html中执行保存操作时,能够通知index.html保存的文本内容。
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>iframe.html</title>
</head>
<body>

    <textarea id="textareaRef" rows="5" cols="33">
      Hello World
    </textarea>
  <button onclick="saveHandle()">保存</button>
  
  <script>
    function saveHandle() {

    }
  </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>index.html</title>
</head>
<body>
  <div class="iframe-wrapper">
    <iframe id="iframeRef" height="400px" width="600px" src="http://127.0.0.1:5500/iframe-demo/iframe.html"></iframe>
  </div>
  <div class="insert-wrapper">
    <input id="inputRef" type="text" /><button onclick="insertHandle()">插入</button>
  </div>
  <script>
    function insertHandle() {

    }
  </script>
</body>
</html>

        项目运行后,效果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

window.postMessage

        实现 ifrmae 通信的一种实现方案是利用window.postMessage,该 API 是 HTML5 引入的一个用于安全地实现跨源通信的解决方案。

        其基本使用语法如下:

otherWindow.postMessage(message, targetOrigin, [transfer]);
  • otherWindow:指向接收消息的窗口的引用,比如 iframe 的 contentWindow 属性
  • message:将要发送到其他 window 的数据。
  • targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串 “*”(表示无限制)或者一个 URI。
  • transfer(可选):是一串和 message 同时传递的 Transferable 对象。

        在目标窗口中,可以监听 message 事件来接收消息:

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event) {
  // 对于任何不是预期来源的消息,我们忽略它
  if (event.origin !== "http://example.org:8080") {
    return;
  }
  
  // 处理事件,可以使用 event.data 获取发送的数据
}

        在学习了 window.postMessage 的基本使用后,接下来我们便利用该 API 实现我们的需求。

父传子

        首先,我们通过 iframeRef 获取与之关联的 window 对象。接着,利用这个对象调用 postMessage 方法来发送消息。

// index.html

let iframeWindow = iframeRef.contentWindow;
function insertHandle() {
  iframeWindow.postMessage({
    event: 'insert',
    data: inputRef.value,
  }, '*');
}

        为了能够接收发送方的消息,接收方还需要注册一个 message 事件监听器。

// iframe.html

window.addEventListener('message', (e) => {
  if (e.data.event === 'insert') {
    insertTextAtCursor(textareaRef, e.data.data);
  }
});

function insertTextAtCursor(element, text) {
  // 获取光标位置
  var startPos = element.selectionStart;
  var endPos = element.selectionEnd;
  // 保存原始文本
  var beforeText = element.value.substring(0, startPos);
  var afterText = element.value.substring(endPos);
  // 插入文本
  element.value = beforeText + text + afterText;
  // 恢复光标位置
  element.selectionStart = startPos + text.length;
  element.selectionEnd = startPos + text.length;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

子传父

        若要在 iframe.html 中向 index.html 传递消息,首先需要获取 index.html 的窗口引用。可以使用以下两个 API 来实现这一目的:

  1. window.top: 这个属性引用的是最顶层的窗口,即包含当前窗口或框架的最外层窗口。例如,如果 index.html 嵌入了 demo.html,而 demo.html 又嵌入了 iframe.html,那么在 iframe.html 中要获取 index.html 的窗口引用,就可以使用 window.top

  2. window.parent:这个属性引用的是当前窗口或框架的直接父窗口。在当前项目中,由于 iframe.html是直接嵌入在 index.html 中的,因此在 iframe.html 中通过 window.parent API 就可以获取到 index.html 的窗口引用。

        成功获取父窗口的引用之后,我们可以利用 postMessage 方法来发送消息。

// iframe.html

function saveHandle() {
  window.parent.postMessage({
    event: 'save',
    data: textareaRef.value
  }, '*');
}

        在另一端,父窗口则需要设置一个 message 事件监听器,以便能够接收并处理从子窗口传递过来的消息。

// index.html

window.addEventListener('message', (e) => {
  if (e.data.event === 'save') {
    alert(e.data.data);
  }
});

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

        至此,我们通过 window.postMessage 完成了网页与内嵌网页的通信。

dom 操作

        第二种iframe通信方式是通过DOM操作实现的。然而,这种方法具有一定的局限性,它要求iframe和父页面必须位于同一主域名下。此外,为了实现通信,需要在父页面和iframe页面中分别设置 document.domain 属性为相同的主域名。

// 在父页面中
document.domain = 'example.com';

// 在 iframe 中
document.domain = 'example.com';

        我们可以通过访问iframecontentDocument 属性来获取其内部的DOM节点。需要注意的是,这一操作必须等待iframe完全加载完成后才能进行,以确保能够成功获取到DOM节点。

// index.html

document.domain = '127.0.0.1';
let contentDocument = iframeRef.contentDocument;

let textareaRef;

iframeRef.onload = function() {
  const contentDocument = iframeRef.contentDocument;
  textareaRef = contentDocument.querySelector('.textareaRef');
  const buttonRef = contentDocument.querySelector('button');

  buttonRef.addEventListener('click', () => {
    alert(textareaRef.value);
  });
};

function insertHandle() {
  insertTextAtCursor(textareaRef, inputRef.value);
}

function insertTextAtCursor(element, text) {
  // 获取光标位置
  var startPos = element.selectionStart;
  var endPos = element.selectionEnd;
  // 保存原始文本
  var beforeText = element.value.substring(0, startPos);
  var afterText = element.value.substring(endPos);
  // 插入文本
  element.value = beforeText + text + afterText;
  // 恢复光标位置
  element.selectionStart = startPos + text.length;
  element.selectionEnd = startPos + text.length;
}
// iframe.html

document.domain = '127.0.0.1';

总结

        window.postMessage更适合用于跨源通信,尤其是需要在不同窗口之间安全地传递数据时。而iframe.contentDocument主要用于同源策略下的DOM操作。

项目实战

        在深入理解了iframe的通信原理之后,我们将其应用于一项具体项目中。该项目背景是:我们从前端获取后端数据,并通过表格形式进行展示。尽管表格展示数据方便,但其处理能力有限。为了突破这一局限,我们引入了univer,以此增强数据处理和展示的灵活性及功能性。

univer-sheet-plugin

        首先,初始化一个项目 univer-sheet-plugin,该项目作为一个插件被其他项目集成,通过接收上层的数据来渲染数据并集成表格处理能力。

pnpm dlx degit dream-num/univer-sheet-start-kit univer-sheet-plugin

cd univer-sheet-plugin
pnpm i

pnpm run dev

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

        接下来我们需要做的则是,接收传递给当前窗口的消息,并将数据渲染到 sheet 中。

// univer-sheet-plugin/src/main.ts

import './style.css'

import { setupUniver } from './setup-univer'
import { setupToolbar } from './setup-toolbar'

function main() {
  const univerAPI = setupUniver()
  setupToolbar(univerAPI)

  window.addEventListener('message', (event) => {
    if (event.data.event === 'insert') {
      const values = event.data.data;
      const activeWorkbook = univerAPI.getActiveWorkbook();
      const activeSheet = activeWorkbook?.getActiveSheet()
      const range = activeSheet?.getRange(0, 0, values.length, values[0].length)
      if (range) {
        range.setValues(values);
      }
    }
  });
}

main()

my-project

pnpm create vue@latest
<template>
  <div class="container">
    <el-table :data="tableData" border style="width: 100%">
      <el-table-column prop="date" label="Date" width="180" />
      <el-table-column prop="name" label="Name" width="180" />
      <el-table-column prop="address" label="Address" />
    </el-table>
    <el-button type="primary" @click="visible = true">使用 univer 打开</el-button>
  </div>
  <UniverDialog v-model="visible" :table-data="tableData" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import UniverDialog from './components/univer-dialog.vue';

const visible = ref(false);

const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
  },
];
</script>

<style scoped>
  .el-button {
    margin-top: 20px;
  }
</style>
<template>
  <ElDialog v-model="visible" width="80vw">
    <iframe 
      width="100%" 
      height="800px" 
      ref="iframeRef" 
      src="http://localhost:5173/"
      @load="onIframeLoad"  
    />
  </ElDialog>
</template>

<script lang='ts' setup>
import { ElDialog } from 'element-plus';
import { watch } from 'vue';
import { ref } from 'vue';

const props = defineProps<{
  tableData: Array<object>,
}>();

const visible = defineModel<boolean>()

const iframeRef = ref();
const iframeLoaded = ref(false);

const onIframeLoad = () => {
  console.log('onIframeLoad');
  iframeLoaded.value = true;
  if (visible.value) {
    postMessageToIframe();
  }
};

watch(() => visible.value, (val) => {
  if (val && iframeLoaded.value) {
    postMessageToIframe();
  }
});


const postMessageToIframe = () => {
  const iframeWindow = iframeRef.value.contentWindow;
  if (iframeWindow) {
    iframeWindow.postMessage({
      event: 'insert',
      data: props.tableData.map(obj => Object.values(obj)),
    }, '*');
  }
};
</script>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

         至此,我们便完成了使用 iframe 去扩展我们的项目。

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Bootstrap中文管理后台模板是一个非常流行的前端开发框架,适用于构建各类网站、管理系统等项目。其中,iframe是一种常见的HTML元素,用于在当前页面中嵌入其他网页或文档。 在Bootstrap中文管理后台模板中,可以通过以下方式来定义和应用iframe的样式: 1. `<iframe>`元素:在HTML文件中使用`<iframe>`元素来创建iframe。可以设置iframe的属性,如`src`指定嵌入网页的URL、`width`指定宽度、`height`指定高度等。可以使用Bootstrap提供的CSS类,如`.img-fluid`使iframe保持响应式。 2. CSS样式表:可以使用自定义的CSS样式表来为iframe添加样式。可以通过选择器选中iframe元素,并设置样式,如`background-color`指定背景颜色、`border`添加边框、`margin`设置外边距等。也可以利用Bootstrap提供的CSS类,如`.p-2`添加内边距、`.rounded`应用圆角等。 3. 内联样式:也可以直接在`<iframe>`标签中添加内联样式来定义iframe的样式。可以在标签中使用`style`属性,设置样式属性和属性值,如`style="width: 100%; height: 500px; border: none;"`。 4. 嵌入式样式:可以在HTML文件中使用`<style>`标签定义嵌入式样式,然后将定义的样式应用于iframe。可以在`<style>`标签中使用选择器选中iframe,设置样式属性和属性值。 通过以上方式,可以灵活地为Bootstrap中文管理后台模板中的iframe元素添加样式,从而实现自定义的iframe样式。同时,可以充分利用Bootstrap提供的CSS类和其他定制化样式,使页面的iframe部分更加美观和易用。 ### 回答2: Bootstrap中文管理后台模板提供了一些内置的样式和工具,可以用于自定义和美化iframe元素。 首先,我们可以通过Bootstrap中文管理后台模板提供的CSS类来设置iframe元素的样式。例如,可以使用class="embed-responsive embed-responsive-16by9"将iframe设置为响应式的16:9比例,使其自动适应不同屏幕大小。此外,我们还可以使用class="embed-responsive-item"将样式应用到iframe元素的父容器上。 其次,Bootstrap中文管理后台模板还提供了一些颜色类,可以为iframe元素添加不同的背景颜色。我们可以通过使用class="bg-primary"、class="bg-secondary"等类来设置iframe的背景色,实现自定义的效果。 此外,Bootstrap中文管理后台模板还支持使用自定义的CSS样式来进一步美化iframe元素。我们可以通过给iframe元素添加ID或class属性,并在自定义的CSS文件中定义相应的样式规则,来实现更加个性化的效果。 需要注意的是,在使用iframe时,我们应保证其内容符合网站的安全要求,避免潜在的信息安全风险。同时,还应根据实际需求合理使用iframe,在维护用户体验和性能方面做出权衡。 总之,Bootstrap中文管理后台模板提供了一些内置的样式和工具,可以帮助我们美化和自定义iframe元素的样式。通过合理地利用这些功能,我们可以实现一个美观、高效的管理后台页面。 ### 回答3: Bootstrap是一个流行的前端开发框架,它提供了丰富的组件和样式,用于构建响应式和美观的网页。在Bootstrap中,通常使用iframe元素来嵌入其他网页或内容。 在管理后台模板中使用iframe样式,可以实现以下效果: 1. 设置iframe的宽度和高度:可以通过设置iframe的width和height属性来控制其显示的宽度和高度。例如,可以使用style属性为iframe指定宽度和高度: <iframe src="目标网页链接" style="width:100%;height:500px;"></iframe> 这样可以使iframe填充整个容器,并设置高度为500像素。 2. 设置iframe的边框和样式:可以使用CSS样式来设置iframe的边框、边框颜色和其他样式。可以通过为iframe添加class或id,并在样式表中定义相应的样式来实现。例如: <iframe src="目标网页链接" class="iframe-style"></iframe> 在样式表中定义: .iframe-style { border: 1px solid #ccc; border-radius: 5px; padding: 10px; } 3. 设置iframe的滚动条:当嵌入的网页内容超出iframe的显示区域时,可以设置是否显示滚动条。可以通过设置iframe的scrolling属性来实现。例如,设置滚动条垂直显示: <iframe src="目标网页链接" scrolling="yes"></iframe> 当内容超出iframe的高度时,将显示垂直滚动条。 综上所述,通过合理的设置iframe的宽度、高度、样式和滚动条等属性,可以实现在Bootstrap管理后台模板中嵌入iframe并实现自定义的样式效果。这样可以方便地在同一个界面中展示不同的网页内容,提升用户体验和功能扩展性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值