晓怒(xiao.nu),记录分享我的学习笔记,学习一下,记录一下,分享一下。

告别 iframe 视频高度烦恼:纯 CSS + JS 实现完美自适应

在现代网页设计中,嵌入外部视频内容(如B站、YouTube视频)是常见的需求。然而,一个普遍的痛点是:如何让这些 iframe 嵌入的视频在不同设备和屏幕尺寸下保持正确的宽高比,实现高度自适应?

你可能遇到过这样的情况:在PC端看起来正常的视频,到了手机端却因为高度固定而显得上下留白过多或内容被裁剪。这是因为 iframe 默认的 height 属性通常是固定的像素值,而 width: 100% 只解决了宽度自适应问题,高度却纹丝不动。

本文将深入探讨这个问题,并提供一个优雅且高效的纯 CSS + JavaScript 解决方案,最终将其封装为一个可复用的“插件”。

问题所在:固定高度与响应式宽度

当我们将 iframe 的宽度设置为 width: 100% 时,它会根据父容器的宽度进行伸缩。但如果其 height 属性被设置为一个固定值(例如 height: 300px),那么无论 iframe 的宽度如何变化,其高度都保持不变。这在屏幕尺寸变化时,会导致视频的宽高比失真,影响用户体验。

例如:

  • PC端: 宽度足够,固定高度可能看起来正常。
  • 移动端: 宽度变窄,但高度不变,视频内容可能会被挤压,或者出现大量上下留白。

核心原理:padding-bottom 的巧妙运用

解决这个问题的关键在于利用 CSS 中一个鲜为人知的特性:padding-bottom(或 padding-top)属性的百分比值是相对于父元素的宽度来计算的。

我们可以利用这一点来创建一个“占位符”容器,其高度始终与其宽度保持一定的比例。

假设我们希望视频保持常见的 16:9 比例(宽16,高9):

  • 高度是宽度的 9/16
  • 9 / 16 * 100% = 56.25%

因此,如果我们将父容器的 padding-bottom 设置为 56.25%,那么这个容器的高度就会始终是其宽度的 56.25%,从而完美地模拟了 16:9 的宽高比。

CSS 核心代码:

.iframe-auto-height-wrapper {
    position: relative; /* 为内部的绝对定位元素提供定位上下文 */
    padding-bottom: 56.25%; /* 关键:根据父宽度计算高度,保持16:9比例 */
    height: 0; /* 将容器自身高度设为0,让padding-bottom撑开高度 */
    overflow: hidden; /* 隐藏超出容器的内容 */
    background-color: #000; /* 视频加载前的背景色,可选 */
    border-radius: 4px; /* 轻微圆角,可选 */
}

.iframe-auto-height-wrapper iframe {
    position: absolute; /* 绝对定位,脱离文档流,不占据空间 */
    top: 0;
    left: 0;
    width: 100%; /* 填充父容器的宽度 */
    height: 100%; /* 填充父容器的高度,父容器的高度由padding-bottom撑开 */
    border: none; /* 移除iframe默认边框 */
}

原理拆解:

  1. .iframe-auto-height-wrapper (父容器):

    • position: relative;: 这是为了让内部的 iframe 可以相对于这个容器进行绝对定位。
    • padding-bottom: 56.25%;: 这是魔法所在!它根据容器的宽度计算出一个高度,确保宽高比是 16:9。
    • height: 0;: 确保容器的实际高度完全由 padding-bottom 撑开,而不是由其内容或其他因素影响。
    • overflow: hidden;: 防止任何溢出内容破坏布局。
  2. .iframe-auto-height-wrapper iframe (子 iframe):

    • position: absolute;: 将 iframe 从文档流中取出,使其不占据空间,并可以精确放置。
    • top: 0; left: 0;: 将 iframe 定位到其父容器的左上角。
    • width: 100%; height: 100%;: 让 iframe 完全填充其父容器(也就是那个由 padding-bottom 撑起来的、保持了 16:9 比例的“盒子”)。

这样,无论外部容器的宽度如何变化,iframe 都能自动调整其高度以保持正确的宽高比。

将逻辑封装为独立的 JavaScript 文件(“插件”化)

为了方便在任意网页中复用此功能,我们可以将上述 CSS 和动态包裹 iframe 的 JavaScript 逻辑封装到一个独立的 .js 文件中。

iframeAutoHeight.js 文件内容:

/**
 * iframe 视频高度自适应插件
 * 自动为页面中的 iframe 元素添加自适应高度样式和包裹结构。
 *
 * 使用方法:
 * 将此文件作为 <script> 标签引入到你的 HTML 页面中,建议放在 </body> 标签之前。
 * <script src="path/to/iframeAutoHeight.js"></script>
 *
 * 默认处理页面中所有的 'iframe' 标签。
 * 此脚本使用 IIFE (Immediately Invoked Function Expression) 模式,
 * 避免全局变量污染,并在 DOMContentLoaded 事件后自动执行。
 */
(function() { // 使用 IIFE 避免全局变量污染

    // 1. 动态注入 CSS 样式
    const styleId = 'iframe-auto-height-styles';
    // 检查是否已经注入过样式,避免重复添加
    if (!document.getElementById(styleId)) {
        const style = document.createElement('style');
        style.id = styleId;
        style.textContent = `
            /* iframe 视频自适应的核心CSS */
            .iframe-auto-height-wrapper {
                position: relative;
                padding-bottom: 56.25%; /* 16:9 视频比例 (9 / 16 * 100%) */
                height: 0; /* 将容器自身高度设为0,让padding-bottom撑开高度 */
                overflow: hidden; /* 隐藏超出容器的内容 */
                background-color: #000; /* 视频加载前的背景色 */
                border-radius: 4px; /* 轻微圆角 */
            }

            .iframe-auto-height-wrapper iframe {
                position: absolute; /* 绝对定位,脱离文档流 */
                top: 0;
                left: 0;
                width: 100%; /* 填充父容器的宽度 */
                height: 100%; /* 填充父容器的高度,父容器的高度由padding-bottom撑开 */
                border: none; /* 移除iframe默认边框 */
            }
        `;
        // 将样式添加到 <head> 标签中
        document.head.appendChild(style);
    }

    // 2. 遍历并包裹 iframe 元素
    // 确保在 DOM 完全加载后执行,避免找不到 iframe 元素
    document.addEventListener('DOMContentLoaded', function() {
        const selector = 'iframe'; // 默认处理所有 iframe 标签
        const iframes = document.querySelectorAll(selector);

        iframes.forEach(iframe => {
            // 检查 iframe 是否已经被处理过,避免重复包裹
            // 使用 data-* 属性标记,防止多次调用或在动态内容加载时重复处理
            if (iframe.dataset.autoHeightProcessed) {
                return;
            }

            // 再次检查父元素是否已经是我们的 wrapper,以防万一
            if (iframe.parentNode && iframe.parentNode.classList.contains('iframe-auto-height-wrapper')) {
                iframe.dataset.autoHeightProcessed = 'true';
                return;
            }

            const wrapper = document.createElement('div'); // 使用 div 作为包裹容器更通用
            wrapper.classList.add('iframe-auto-height-wrapper');

            // 确保 iframe 有父节点,然后进行包裹操作
            if (iframe.parentNode) {
                // 将新的包裹容器插入到 iframe 的前面
                iframe.parentNode.insertBefore(wrapper, iframe);
                // 将 iframe 移动到新的包裹容器内部
                wrapper.appendChild(iframe);
                // 标记 iframe 已被处理
                iframe.dataset.autoHeightProcessed = 'true';
            }
        });
    });

})(); // 立即执行这个匿名函数

代码解析:

  1. IIFE (Immediately Invoked Function Expression): (function() { ... })(); 这种模式可以创建一个独立的作用域,避免变量污染全局环境。
  2. 动态注入 CSS:

    • 脚本首先检查一个带有特定 id (iframe-auto-height-styles) 的 <style> 标签是否已经存在。
    • 如果不存在,它会创建一个新的 <style> 标签,将所有必要的 CSS 规则作为 textContent 注入,并将其添加到文档的 <head> 部分。这确保了 CSS 只会被加载一次。
  3. DOMContentLoaded 事件监听:

    • document.addEventListener('DOMContentLoaded', function() { ... }); 确保了包裹 iframe 的逻辑只会在整个 HTML 文档(DOM)被完全加载和解析后才执行。这样可以保证脚本能够找到页面上所有的 iframe 元素。
  4. 遍历与包裹:

    • document.querySelectorAll('iframe') 获取页面中所有的 iframe 元素。
    • forEach 循环遍历每个 iframe
    • 健壮性处理:

      • iframe.dataset.autoHeightProcessed: 通过设置一个自定义的 data-* 属性来标记已经被处理过的 iframe,防止重复包裹。
      • iframe.parentNode.classList.contains('iframe-auto-height-wrapper'): 额外检查 iframe 是否已经被我们特定的 wrapper 包裹,进一步增强代码的健壮性。
    • document.createElement('div'): 创建一个新的 div 元素作为 iframe 的包裹容器。使用 div<p> 更通用,因为 iframe 是块级元素。
    • wrapper.classList.add('iframe-auto-height-wrapper'): 为新的包裹容器添加样式类。
    • insertBeforeappendChild: 这两步操作将 iframe 从其原始位置移动到新创建的 wrapper 内部。

如何在你的网页中使用?

  1. 将上述 JavaScript 代码保存为 iframeAutoHeight.js 文件。
  2. 将这个 iframeAutoHeight.js 文件放置在你的网站目录下(例如,放在 js/ 文件夹内)。
  3. 在你需要应用此效果的任何 HTML 页面中,在 </body> 结束标签之前添加以下 <script> 标签:

    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>我的响应式页面</title>
        <!-- 其他CSS样式 -->
    </head>
    <body>
    
        <!-- 你的页面内容和 iframe 标签 -->
        <div class="video-section">
            <h2>我的B站视频</h2>
            <iframe src="https://player.bilibili.com/player.html?isOutside=true&aid=115042208979023&bvid=BV1h8YkzBEid&cid=31749967605&p=1"
                    scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true">
            </iframe>
        </div>
    
        <!-- 引入 iframeAutoHeight.js 文件 -->
        <!-- 确保路径正确 -->
        <script src="js/iframeAutoHeight.js"></script>
    
    </body>
    </html>

就是这么简单!现在,无论你的用户使用什么设备访问你的网页,嵌入的 iframe 视频都能优雅地自适应其高度,保持完美的宽高比,大大提升用户体验。告别 iframe 视频高度烦恼,从现在开始!

快来做第一个评论的人吧~