使Iframe适合容器剩余的高度达到100%。

343

我想设计一个带有横幅和iframe的网页。我希望iframe可以填满剩余的页面高度,并在浏览器调整大小时自动调整大小。是否可以仅使用CSS而不编写JavaScript代码来完成它?

我尝试在iframe上设置height:100%,结果非常接近,但是iframe尝试填充整个页面高度,包括横幅div元素的30px高度,因此我得到了不必要的垂直滚动条。这还不够完美。

我尝试使用CSS margin、padding属性成功地占据整个网页的剩余高度,但这个技巧在iframe上没有起作用。

 <body>
    <div style="width:100%; height:30px; background-color:#cccccc;">Banner</div>
    <iframe src="http: //www.google.com.tw" style="width:100%; height:100%;"></iframe>
</body>

28个回答

329

2019年更新

简短概述:现在最好的选择是使用flexbox。所有浏览器都已经很好地支持它了,而且已经支持多年。建议使用这个选项并且不要再考虑其他方案。以下是一个flexbox的代码示例:

body, html {width: 100%; height: 100%; margin: 0; padding: 0}
.row-container {display: flex; width: 100%; height: 100%; flex-direction: column; background-color: blue; overflow: hidden;}
.first-row {background-color: lime; }
.second-row { flex-grow: 1; border: none; margin: 0; padding: 0; }
<div class="row-container">
  <div class="first-row">
    <p>Some text</p>
    <p>And some more text</p>
  </div>
  <iframe src="https://jsfiddle.net/about" class="second-row"></iframe>
</div>

以下内容仅供学习和历史参考。


诀窍在于理解100%是基于什么计算的。阅读CSS规范可以帮助你理解。

简而言之 - 有一种东西叫做“包含块”,它不一定是父元素。简单来说,它是层级结构中第一个具有position:relative或position:absolute属性的元素。如果没有其他元素,则为body元素本身。因此,当您说“width: 100%”时,它会检查“包含块”的宽度,并将您的元素的宽度设置为相同大小。如果有其他东西存在,那么您可能会得到比其自身更大的“包含块”内容(从而“溢出”)。

高度的工作方式也相同。唯一的例外是,您无法使高度达到浏览器窗口的100%。可以计算出100%的最顶层元素是body(或html?不确定)元素,它只需要足够大来容纳其内容。在其上指定height:100%将没有任何效果,因为它没有“父元素”来测量100%。窗口本身不计入其中。;)

要使某些内容正好拉伸到窗口的100%,您有两个选择:

  1. 使用JavaScript
  2. Don't use DOCTYPE. This is not a good practice, but it puts the browsers in "quirks mode", in which you can do height="100%" on elements and it will stretch them to the window size. Do note, that the rest of your page will probably have to be changed too to accommodate for the DOCTYPE changes.

    Update: I'm not sure if I wasn't wrong already when I posted this, but this certainly is outdated now. Today you can do this in your stylesheet: html, body { height: 100% } and it will actually stretch to the whole of your viewport. Even with a DOCTYPE. min-height: 100% could also be useful, depending on your situation.

    And I wouldn't advise anyone to make a quirks-mode document anymore either, because it causes way more headaches than solves them. Every browser has a different quirks-mode, so getting your page to look consistently across browsers becomes two orders of magnitude more difficult. Use a DOCTYPE. Always. Preferably the HTML5 one - <!DOCTYPE html>. It's easy to remember and works like a charm in all browsers, even the 10 years old ones.

    The only exception is when you have to support something like IE5 or something. If you're there, then you're on your own anyway. Those ancient browsers are nothing like the browsers today, and little advice that is given here will help you with them. On the bright side, if you're there, you probably just have to support ONE kind of browser, which gets rid of the compatibility problems.

    Good luck!

    Update 2: Hey, it's been a long time! 6 years later, new options are on the scene. I just had a discussion in the comments below, here are more tricks for you that work in today's browsers.

    Option 1 - absolute positioning. Nice and clean for when you know the precise height of the first part.

    body, html {width: 100%; height: 100%; margin: 0; padding: 0}
    .first-row {position: absolute;top: 0; left: 0; right: 0; height: 100px; background-color: lime;}
    .second-row {position: absolute; top: 100px; left: 0; right: 0; bottom: 0; background-color: red }
    .second-row iframe {display: block; width: 100%; height: 100%; border: none;}
    <div class="first-row">
      <p>Some text</p>
      <p>And some more text</p>
    </div>
    <div class="second-row">
      <iframe src="https://jsfiddle.net/about"></iframe>
    </div>

    Some notes - the second-row container is needed because bottom: 0 and right: 0 doesn't work on iframes for some reason. Something to do with in being a "replaced" element. But width: 100% and height: 100% works just fine. display: block is needed because it's an inline element by default and whitespace starts creating weird overflows otherwise.

    Option 2 - tables. Works when you don't know the height of the first part. You can use either actual <table> tags or do it the fancy way with display: table. I'll go for the latter because it seems to be in fashion these days.

    body, html {width: 100%; height: 100%; margin: 0; padding: 0}
    .row-container {display: table; empty-cells: show; border-collapse: collapse; width: 100%; height: 100%;}
    .first-row {display: table-row; overflow: auto; background-color: lime;}
    .second-row {display: table-row; height: 100%; background-color: red; overflow: hidden }
    .second-row iframe {width: 100%; height: 100%; border: none; margin: 0; padding: 0; display: block;}
    <div class="row-container">
      <div class="first-row">
        <p>Some text</p>
        <p>And some more text</p>
      </div>
      <div class="second-row">
        <iframe src="https://jsfiddle.net/about"></iframe>
      </div>
    </div>

    Some notes - the overflow: auto makes sure that the row always includes all of its contents. Otherwise floating elements can sometimes overflow. The height: 100% on the second row makes sure it expands as much as it can squeezing the first row as small as it gets.

    Recommended: Option 3 - flexbox - The cleanest one of them all.

    body, html {width: 100%; height: 100%; margin: 0; padding: 0}
    .row-container {display: flex; width: 100%; height: 100%; flex-direction: column; background-color: blue; overflow: hidden;}
    .first-row {background-color: lime; }
    .second-row { flex-grow: 1; border: none; margin: 0; padding: 0; }
    <div class="row-container">
      <div class="first-row">
        <p>Some text</p>
        <p>And some more text</p>
      </div>
      <iframe src="https://jsfiddle.net/about" class="second-row"></iframe>
    </div>

    Some notes - the overflow: hidden is because the iframe still generates some sort of overflow even with display: block in this case. It isn't visible in the fullscreen view or the snippet editor, but the small preview window gets an extra scrollbar. No idea what that is, iframes are weird.


  3. @sproketboy - CSS 最初主要是一种文本格式化语言,具有基本的布局功能,但仍然以文本为中心。它只是比表格更适合布局,因此被广泛采用。从许多方面来看,我们自那时起一直在进行布局方面的“黑客”工作。 - Toni Leigh
    @Vilx- 我已经使用了正确的导航标记,我没有让维护者感到困惑,因为他们会在导航标记中找到导航,也没有让任何机械HTML处理器感到困惑,原因同上。如果我在网页上什么都用表格来做,那会有多难以维护呢?这也不是标签混乱,因为每个标签都有其存在的理由和目的:nav用于标记区域为导航面板;UL和LI用于标记无序列表。浏览器使用类似表格的显示属性,但仅此而已。 - Toni Leigh
    1
    太棒了,谢谢!Flexbox确实是最干净的,但表格也很好学习,而且似乎可以在没有溢出的情况下工作:https://jsfiddle.net/dmitri14/1uqh3zgx/2/ - Dmitri Zaitsev
    1
    将最新的更新移到这个答案的顶部可能值得一试。我不得不浏览整个内容才能找到最好的答案,即Flexbox。 - forgivenson
    2
    @forgivenson - 确实如此。在这个回答的开头添加了一个注释。 - Vilx-
    显示剩余26条评论

    71
    我们使用JavaScript来解决这个问题,以下是源代码。
    var buffer = 20; //scroll bar buffer
    var iframe = document.getElementById('ifm');
    
    function pageY(elem) {
        return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop;
    }
    
    function resizeIframe() {
        var height = document.documentElement.clientHeight;
        height -= pageY(document.getElementById('ifm'))+ buffer ;
        height = (height < 0) ? 0 : height;
        document.getElementById('ifm').style.height = height + 'px';
    }
    
    // .onload doesn't work with IE8 and older.
    if (iframe.attachEvent) {
        iframe.attachEvent("onload", resizeIframe);
    } else {
        iframe.onload=resizeIframe;
    }
    
    window.onresize = resizeIframe;
    

    注意:ifm 是iframe的ID pageY()是由jQuery的作者John Resig创建的。

    53

    另一种方法是在父节点上使用position: fixed;
    如果我没记错的话,position: fixed;将元素绑定到视口,因此,一旦您给此节点width: 100%;height: 100%;属性,它将跨越整个屏幕。从这一点开始,您可以在其中放置<iframe>标签,并使用简单的width: 100%; height: 100%; CSS指令将其跨越剩余空间(宽度和高度)。

    示例代码


        body {
            margin: 0px;
            padding: 0px;
        }
    
        /* iframe's parent node */
        div#root {
            position: fixed;
            width: 100%;
            height: 100%;
        }
    
        /* iframe itself */
        div#root > iframe {
            display: block;
            width: 100%;
            height: 100%;
            border: none;
        }
       <html>
            <head>
                <title>iframe Test</title>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
            </head>
            <body>
                <div id="root">
                    <iframe src="http://stackoverflow.com/">
                        Your browser does not support inline frames.
                    </iframe>
                </div>
            </body>
        </html>


    1
    这个如何获取“剩余”的高度呢? - EricG
    3
    我注意到您还需要在 iframe 中添加 position: fixed - 0xF
    1
    没有滚动条可见 - andrej
    完美-你能进一步解释一下CSS“>”例如div#root > iframe,因为我不熟悉它,找不到参考资料。 - Datadimension
    @andrej 有没有办法同时启用滚动条? - Sebastian

    43

    以下是一些现代方法:


    • Approach 1 - Combination of viewport relative units / calc().

      The expression calc(100vh - 30px) represents the remaining height. Where 100vh is the height of the browser and the usage of calc() effectively displaces the height of the other element.

      Example Here

      body {
          margin: 0;
      }
      .banner {
          background: #f00;
          height: 30px;
      }
      iframe {
          display: block;
          background: #000;
          border: none;
          height: calc(100vh - 30px);
          width: 100%;
      }
      <div class="banner"></div>
      <iframe></iframe>

      Support for calc() here; support for viewport relative units here.


    • Approach 2 - Flexbox approach

      Example Here

      Set the display of the common parent element to flex, along with flex-direction: column (assuming you want the elements to stack on top of each other). Then set flex-grow: 1 on the child iframe element in order for it to fill the remaining space.

      body {
          margin: 0;
      }
      .parent {
          display: flex;
          flex-direction: column;
          min-height: 100vh;
      }
      .parent .banner {
          background: #f00;
          width: 100%;
          height: 30px;
      }
      .parent iframe {
          background: #000;
          border: none;
          flex-grow: 1;
      }
      <div class="parent">
          <div class="banner"></div>
          <iframe></iframe>
      </div>

      Since this approach has less support1, I'd suggest going with the aforementioned approach.

    1虽然在Chrome/FF上看起来可以工作,但在IE上却无法正常工作(第一种方法适用于所有现代浏览器)。


    3
    在我看来,这里提到的两个选项都是最佳方式。由于使用 calc 时需要知道从 vh 减去多少空间,因此我更喜欢使用 flexbox 选项。使用 flexbox 只需简单的 flex-grow:1 即可实现。 - Kilmazing

    38

    你可以使用DOCTYPE来实现,但是需要使用table。看一下这个:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
    <style>
    *{margin:0;padding:0}
    html, body {height:100%;width:100%;overflow:hidden}
    table {height:100%;width:100%;table-layout:static;border-collapse:collapse}
    iframe {height:100%;width:100%}
    
    .header {border-bottom:1px solid #000}
    .content {height:100%}
    </style>
    </head>
    <body>
    <table>
        <tr><td class="header"><div><h1>Header</h1></div></td></tr>
        <tr><td class="content">
            <iframe src="http://google.com/" frameborder="0"></iframe></td></tr>
    </table>
    </body>
    </html>
    

    1
    至少它不需要JS!但它是否安全可靠? - Ali Shakiba
    1
    这种解决方案的缺陷是,html,body{overflow:hidden;}会移除页面滚动。 - Ali Shakiba
    我正在尝试在上述代码中添加页脚,但页脚没有显示。你能帮忙吗? - Akshay Raut

    18

    也许这个问题已经得到了回答(前面有几个答案是“正确”的方法),但我想添加我的解决方案。

    我们的iFrame在一个div内加载,因此我需要另一种方法来获取高度而不是使用window.height。考虑到我们的项目已经大量依赖于jQuery,我认为这是最优雅的解决方案:

    $("iframe").height($("#middle").height());
    

    当然,"#middle"是div的id。你需要做的唯一额外的事情是,每当用户调整窗口大小时,重新调整这个大小。

    $(window).resize(function() {
        $("iframe").height($("#middle").height());
    });
    

    8
    另一个选择是使用 vh
    <iframe src='/' style="display:block; border:none; height:100vh; width:100%;"></iframe>
    

    经过多次失败的尝试和大量的研究,这对我来说是答案。我需要将一个Dash应用程序传递给Flask应用程序。非常感谢。 - undefined

    6

    MichAdel的代码对我来说可用,但我进行了一些小修改以使其正常工作。

    function pageY(elem) {
        return elem.offsetParent ? (elem.offsetTop + pageY(elem.offsetParent)) : elem.offsetTop;
    }
    var buffer = 10; //scroll bar buffer
    function resizeIframe() {
        var height = window.innerHeight || document.body.clientHeight || document.documentElement.clientHeight;
        height -= pageY(document.getElementById('ifm'))+ buffer ;
        height = (height < 0) ? 0 : height;
        document.getElementById('ifm').style.height = height + 'px';
    }
    window.onresize = resizeIframe;
    window.onload = resizeIframe;
    

    6

    你正在展示一个iframe,其高度相对于容器(即body)为100%。这是正确的。

    可以尝试以下代码:

    <body>
      <div style="width:100%; height:30px; background-color:#cccccc;">Banner</div>
      <div style="width:100%; height:90%; background-color:transparent;">
        <iframe src="http: //www.google.com.tw" style="width:100%; height:100%;">
        </iframe> 
      </div>
    </body>
    

    当然,将第二个 div 的高度更改为您想要的高度。

    2
    可以使用calc(100%-30px)代替90%。 - Justin E

    6

    这是我所做的。我遇到了同样的问题,最终在网上搜索了几个小时的资源。

    <style type="text/css">
       html, body, div, iframe { margin:0; padding:0; height:100%; }
       iframe { position:fixed; display:block; width:100%; border:none; }
    </style>
    

    我将这段代码添加到了头部。

    请注意,我的 iframe 位于一个只有 3 行和 1 列的表格的中间单元格内。


    你的代码只有在包含IFRAME的TD具有height:100%时才能正常工作。如果表格包含在DIV元素中,它将起作用,因为您为所有div设置了height:100%的样式。 - Nikola Petkanski

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接