修身养性,知行合一

  • 首页
  • 爱码
    • 系统
    • 数据库
    • JavaScript
    • CSharp
    • Python
  • 生活
    • 文化
    • 美食
  • 杂谈
  • 关于
修身养性,知行合一
码字,杂谈
  1. 首页
  2. 爱码
  3. 前端
  4. JavaScript
  5. 正文

嵌套其他页面内容并自适应高度

2024年8月21日 2692点热度 0人点赞 0条评论

需求

有时候我们有这样的需求:我们需要挂载一个链接地址内容到本页面。后台只会返给你一个 url 链接,此时能确定的实现方案就是利用 iframe 来渲染这个链接。比如这样:

<iframe width="100%" height="500px" frameBorder="0" src="url" />

这样就可以解决问题。

高度问题

我们知道,iframe 默认高度为150,我们经常需要自定义高度。一般来说,高度来源就两种方式:

  • 子页面提供
  • 父页面自己获取

当然,子页面内容如果相对固定,也可以直接写死,这里就不讨论了。

比较简单的方式是让父页面自己获取,我们可以利用 onLoad 事件来实现:

function adjustIframeHeight() {
  if (this.$refs.iframeRef) {
  this.$refs.iframeRef.style.height = this.$refs.iframeRef.contentWindow.document.body.scrollHeight + 'px';
  }
}

在元素中,这样写:

<iframe src="url" width="100%" frameBorder="0" @load="adjustIframeHeight" ref="iframeRef" />

此时,就可以让 iframe 在加载后自动计算高度,然后让其自动匹配内部高度。

好了么?其实这里还有一个问题,就是当子页面的 url 与当前页面不同源时,浏览器出于安全原因,不允许父页面获取子页面的 document:

file

这也就无法获取到子页面的正确高度。此时,就需要子页面主动提供。如果子页面也是可控的,那么修改一下,让其内部加载完毕后发送一个 postMessage,父页面就可以正常接收到。这个方式也是通常使用 iframe 解决跨域的方式之一,这里就不赘述了。有了子页面提供的高度,那么父元素直接拿来用就好了,不会出现任何问题。

但是!!!如果我们需要挂的是外网地址,页面不是我们的,我们无法控制,但同样有跨域的问题,获取不到高度,页面就不能自适应子页面,而且子页面链接还可能不同,我们不可能为每一个子页面出一套对应的高度样式。

没有办法了么?前端解决不了的,让后端来解决。

使用代理

针对上面 buff 叠满的情况,我们只有一条路走,那就是父页面自主获取子页面高度,但是因为跨域问题,导致我们无法获取到子页面的docuemnt,那我们就只能让它们同源~!

让后端同学代理这个地址,这样,我们的链接就同源了。这里以 node 为例:

const express = require('express');
const axios = require('axios');
const app = express();
const port = 3000;

app.use(express.static('public'));

app.get('/proxy', async (req, res) => {
  const url = req.query.url;
  if (!url) {
    return res.status(400).send('URL is required');
  }

  try {
    const response = await axios.get(url);
    res.send(response.data);
  } catch (error) {
    res.status(500).send('Error fetching the URL');
  }
});

app.listen(port, () => {
  console.log(`Proxy server listening at http://localhost:${port}`);
});

让服务器接收这个链接,然后将内容完全返回。在前端,我们只需要将这个 url 拼接到这个服务地址的参数中即可:

<iframe src="http://localhost:8080/proxy?url=xxx" width="100%" frameBorder="0" />

此时,我们再用 onLoad 事件监听,就可以轻松获取到 contentWindow 中的 document,此时,我们就可以任意调整 iframe 元素的高度,以适配内部子页面的高度,从而解决问题。

放开思路

还有一种其他途径,我们可以直接放弃使用 iframe,通过直接获取子页面内容,自行截取需要的内容,比如 <body>...</body> 部分,将其适当修改后,添加到我们的页面中:

// 获取 url 页面 html
fetch('url', { credentials: "include", crossDomain: true}).then(res => return res.text()).then(content => console.log(content));

获取到完整的页面信息后,通过各种方式,比如正则匹配,可以轻松获取到目标元素(这里就不展开怎么获取了),就可以利用渲染机制,将文本渲染到页面中,比如 vue:

<div v-html="content" />

我们可以在这个 div 中添加任意样式。

摒弃 iframe

我一直竭尽所能摒弃 iframe。原因有三:

  • 样式问题,比如上面出现的 height。当然,必须要承认,iframe 在样式隔离上有一定优势
  • 内容问题。它是独立渲染一个页面,当内部有一些 js 逻辑,比如强制跳转的时候,父页面无法预期和控制
  • 交互问题。父子页面无法便利的做到类似滚动交互、点击交互,这些都需要利用 postMessage 传递参数来模拟实现,前提是子页面受我们控。

我个人理解,iframe 就不应该出现在 h5 上,它是一个老旧的玩意,曾经风光一时,但仍然无法避免它现在的短板。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: JavaScript
最后更新:2024年8月21日

jeremyjone

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

取消回复

文章目录
  • 需求
  • 高度问题
  • 使用代理
  • 放开思路
  • 摒弃 iframe
最新 热点 随机
最新 热点 随机
node-sass 的安装 解决端口被占的问题 vue3 组件 Props 的声明方式 给 div 添加选中状态 请求的取消 rgb 颜色小数兼容问题
小技巧系列 - JS判断图片是否已经缓存 resharper2019.3.3最新激活方案 将flask程序部署在apache上 node-sass 的安装 .net core 3.x使用mysql EntityFramework css实现跳动的文字

(っ•̀ω•́)っ✎⁾⁾ 开心每一天

COPYRIGHT © 2021 jeremyjone.com. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

京ICP备19012859号-1

京公网安备 11010802028585号