Iframe嵌套网页

嵌套网页 - Iframe

核心原生作用

  • iframe 的核心原生作用是 "在当前页面嵌入独立的 HTML 文档(嵌套网页)"
    • iframe 是浏览器同源策略下,唯一能合法嵌入跨域页面并实现跨域通信的载体

    • iframe语法 : 该URL指向不同的网页

      html 复制代码
      //iframe 标签  可以在父页面中嵌套子页面
      //iframe src的地址 就是嵌套子页面的地址
      <iframe src="URL"></iframe>

标签属性

  • iframe设置高度与宽度

    • height 和 width 属性用来定义iframe标签的高度与宽度
    • 属性默认以像素为单位, 但是你可以指定其按比例显示 (如:"80%")
    html 复制代码
    <iframe src="demo_iframe.htm" width="200" height="200"></iframe>
  • iframe移除边框

    • frameborder 属性用于定义iframe表示是否显示边框。

    • 设置属性值为 "0" 移除iframe的边框

      html 复制代码
      <iframe 
      	src="demo_iframe.htm" 
      	frameborder="0"
      	style="border: none; outline: none;" <!-- 兜底:防止浏览器默认样式残留 -->
      ></iframe>
  • 使用iframe来显示目标链接页面

    • iframe可以显示一个目标链接的页面
    • 目标链接的属性必须使用iframe的属性,如下实例:
    html 复制代码
    <iframe src="demo_iframe.htm" name="iframe_a"></iframe>
    <p><a href="http://www.bxxx.cn" target="iframe_a">bxxx</a></p>
align - left - right - top - middle - bottom 不赞成使用。请使用样式代替。 规定如何根据周围的元素来对齐此框架。
frameborder - 1 - 0 规定是否显示框架周围的边框。
height - pixels - % 规定 iframe 的高度。
longdesc URL 规定一个页面,该页面包含了有关 iframe 的较长描述。
marginheight pixels 定义 iframe 的顶部和底部的边距。
marginwidth pixels 定义 iframe 的左侧和右侧的边距。
name frame_name 规定 iframe 的名称。
sandbox - "" - allow-forms - allow-same-origin - allow-scripts - allow-top-navigation 启用一系列对 中内容的额外限制。
scrolling - yes - no - auto 规定是否在 iframe 中显示滚动条。
seamless seamless 规定 看上去像是包含文档的一部分。
src URL 规定在 iframe 中显示的文档的 URL。
srcdoc HTML_code 规定在 中显示的页面的 HTML 内容。
width - pixels - % 定义 iframe 的宽度。
allowFullScreen -true -false 授权 iframe 内部的页面 / 元素触发「全屏模式」(比如视频全屏播放、PDF 全屏预览、在线编辑器全屏编辑等)

属性示例

  • 图片
  • 代码
javascript 复制代码
<iframe width="160" height="180" frameborder="0" scrolling="no" 
src="//chongzhi.jd.com/jdhome-czindex-2017.html"></iframe>

常见可监听事件

事件名 触发场景 实用价值
load iframe 页面(包括跨域)加载完成(资源、DOM 都加载完毕) 最常用,用于触发高度自适应、初始化逻辑
error iframe 加载失败(如地址错误、404、网络中断) 兜底处理(显示错误提示、重试按钮)
abort iframe 加载被中断(如用户手动取消、浏览器终止请求) 异常场景处理
loadstart iframe 开始加载资源 显示加载中状态
progress iframe 加载资源过程中(如图片 / 脚本下载) 进度条展示(跨域下仅能感知 "加载中",无法获取具体进度)

不可直接监听的事件(受同源策略限制)

这些是 iframe 内部页面 的 DOM/JS 事件,由于浏览器同源策略禁止父页面访问跨域 iframe 的 contentDocument/contentWindow,因此无法直接绑定监听。

  1. 常见不可直接监听的事件
    内部页面的 click、scroll、input、change、resize 等 DOM 事件;
    内部页面的自定义 JS 事件(如 customEvent);
    内部页面的 window.resize、document.visibilitychange 等全局事件;
    内部页面元素的属性 / 样式变化(如内容高度变化、按钮状态变化)。

  2. 替代方案:postMessage 传递内部事件(核心解决方法)
    通过「内部页面主动发送事件信息 + 父页面监听消息」的方式,实现跨域事件监听,步骤如下:
    步骤 1:内部页面(被嵌套的跨域页面)

    • 监听自身的目标事件,触发时通过 postMessage 向父页面发送事件信息:
    js 复制代码
    // 内部页面的 JS 代码(无论是否是 React,核心是 postMessage)
    // 示例 1:监听内部按钮点击事件
     document.querySelector('#inner-btn').addEventListener('click', () => {
     // 向父页面发送点击事件信息(严格指定父域名,避免泄露)
     window.parent.postMessage(
     { type: 'innerClick', data: { btnId: 'inner-btn', time: Date.now() } },
     'https://父页面的域名.com' // 生产环境务必指定具体域名,不要用 \*
     );
     });
     
     // 示例 2:监听内部页面滚动事件(防抖避免频繁发送)
    const debounce = (fn, delay) => {
    	let timer;
    	return (...args) => {
    	clearTimeout(timer);
    	timer = setTimeout(() => fn(...args), delay);
    	};
    };
    	window.addEventListener('scroll', debounce(() => {
    			window.parent.postMessage(
    			{ type: 'innerScroll', data: { scrollTop: document.documentElement.scrollTop } },
    			'https://父页面的域名.com'
    			);
    }, 100));
    
    // 示例 3:监听内部内容高度变化(用于高度自适应)
    const sendHeight = () => {
    	const height = document.querySelector('#content').offsetHeight;
    	window.parent.postMessage(
    	{ type: 'innerHeightChange', height },
    	'https://父页面的域名.com'
    	);
    };
    // 页面加载/内容变化时发送高度
    window.addEventListener('load', sendHeight);
    document.querySelector('#expand-btn').addEventListener('click', sendHeight);

步骤 2:React 父页面(监听 message 事件)

js 复制代码
import { useRef, useEffect, useState } from 'react';

const CrossDomainIframe = () => {
const [iframeHeight, setIframeHeight] = useState('800px');
const [innerEvents, setInnerEvents] = useState<Array<{ type: string; data: any }>>([]);

// 监听跨域 iframe 发送的 message 事件
useEffect(() => {
const handleMessage = (e: MessageEvent) => {
	// 关键:验证消息来源,防止恶意域名注入
	const allowedOrigins = ['https://内部页面的域名.com'];
	if (!allowedOrigins.includes(e.origin)) return;
    // 处理不同类型的内部事件
    switch (e.data.type) {
      case 'innerHeightChange':
        // 同步内部页面高度
        setIframeHeight(`${e.data.height + 20}px`);
        break;
      case 'innerClick':
        // 记录内部按钮点击事件
        setInnerEvents(prev => [...prev, e.data]);
        console.log('内部按钮点击:', e.data);
        break;
      case 'innerScroll':
        console.log('内部页面滚动:', e.data.scrollTop);
        break;
      default:
        break;
    }
 };

    // 监听全局 message 事件
    window.addEventListener('message', handleMessage);

    // 清理监听
    return () => {
      window.removeEventListener('message', handleMessage);
    };

}, []);

return (

<div>
		<iframe
        src="https://内部页面的域名.com"
        width="100%"
        height={iframeHeight}
        frameBorder="0"
        allowFullScreen={true}
        scrolling="no"
      />
	//展示内部页面传递的事件
	<div>
		<h4>内部页面事件记录:</h4>
		{innerEvents.map((event, idx) => (
			<div key={idx}>{event.type} - {JSON.stringify(event.data)}</div>
		))}
	</div>
</div>
);
};

export default CrossDomainIframe;

iframe跨域&通信

跨域通信:postMessage跨域

  • 同源跨域
    • 当前页面 和 被嵌入的页面 同源
  • 非同源(跨域)场景下:
    • 可以直接监听 iframe 元素本身的原生 DOM 事件(如加载、失败),不受同源策略限制;
    • 无法直接监听 iframe 内部页面的 DOM/JS 事件(如内部按钮点击、滚动、输入框变化),需通过 postMessage 跨域通信间接监听;

iframe组件

ts 复制代码
const AxxIframe: React.FC<{
  src: string;
  width?: number | string;
  height?: number | string;
  query?: string;
}> = ({ src, width = '100%', height = 1000, query }) => {
  return (
    <iframe
      src={src + (query ? `&query=${encodeURIComponent(query)}` : '')}
      width={width}
      height={height}
      allowFullScreen={true}
    />
  );
};
export default AxxIframe;

---------

import { useRef, useState, useEffect } from "react";

const AxxIframe: React.FC<{
  src: string,
  width?: number | string,
  height?: number | string,
  query?: string,
}> = ({ src, width = "100%", height = 1000, query }) => {
  const iframeRef = useRef < HTMLIFrameElement >(null);
  const [iframeHeight, setIframeHeight] = useState < string | number > (height); // 初始高度

  // 计算并设置 iframe 高度
  const calculateIframeHeight = () => {
    if (!iframeRef.current) {
      return;
    }

    try {
      // 1. 获取iframe内部页面的document对象
      const iframeDoc =
        iframeRef.current.contentDocument ||
        iframeRef.current.contentWindow?.document;
      if (!iframeDoc) {
        return;
      }
      // 2. 定位内部页面的「内容区域」(替换为实际的内容区域选择器,如#content)
      const contentArea: any = iframeDoc.querySelector(
        "#pageContainer"
      );
      // 兜底:若找不到内容区域,取整个body高度
      const targetHeight: any =
        contentArea?.offsetHeight || iframeDoc.body.offsetHeight;

      // 3. 设置高度(加少量冗余,避免滚动条)
      if (typeof targetHeight === "number") {
        setIframeHeight(`${targetHeight + 20}px`);
      }
    } catch (e) {
      console.error("获取iframe高度失败(可能跨域):", e);
      // 跨域兜底:可临时写死一个常用高度
      setIframeHeight("800px");
    }
  };

  useEffect(() => {
    const iframe = iframeRef.current;
    console.log("iframe: ", iframeRef);
    if (!iframe) {
      return;
    }

    // 监听iframe加载完成
    iframe.addEventListener("load", calculateIframeHeight);

    // eslint-disable-next-line consistent-return
    return () => {
      iframe.removeEventListener("load", calculateIframeHeight);
    };
  }, [src, query]);

  return (
    <iframe
      ref={iframeRef}
      src={src + (query ? `&query=${encodeURIComponent(query)}` : "")}
      width={width}
      height={iframeHeight}
      style={{ maxHeight: 1000 }}
    />
  );
};
export default AxxIframe;

// 获取iframe高度失败(可能跨域): SecurityError: Failed to read a named property 'document' from 'Window': Blocked a frame with origin "https://xxxx.net" from accessing a cross-origin frame.
// SecurityError: Failed to read a named property 'document' from 'Window': Blocked a frame with origin "https://xxxx-row.net" from accessing a cross-origin frame.
相关推荐
这是个栗子2 天前
【前端知识点总结】前端跨域问题
前端·跨域·cors
闲人编程3 天前
CORS跨域配置与安全策略
中间件·origin·跨域·cors·codecapsule·分离配置·最小权限
Wpa.wk9 天前
自动化测试-多窗口处理 + frame处理
开发语言·javascript·自动化·ecmascript·iframe·frame·多窗口处理
随风一样自由11 天前
React中实现iframe嵌套登录页面:跨域与状态同步解决方案详解
前端·react.js·前端框架·跨域
咋吃都不胖lyh15 天前
<iframe>
iframe
漂流幻境1 个月前
Spring cloud gateway 跨域配置与碰到的问题
java·gateway·springcloud·跨域
linweidong1 个月前
VIVO前端面试题及参考答案
前端·跨域·localstorage·重绘·浏览器兼容·git管理·前端重构
尽兴-1 个月前
[特殊字符] 微前端部署实战:Nginx 配置 HTTPS 与 CORS 跨域解决方案(示例版)
前端·nginx·https·跨域·cors·chrom
一鹿有你们~3 个月前
面试题-前端如何解决跨域
前端·javascript·跨域