将宽度/高度设置为百分比减像素

582

我正在尝试创建一些可重复使用的CSS类,以实现更一致性和更少的杂乱在我的网站上,并且我卡在了尝试标准化我经常使用的一个东西上。

我有一个容器<div>,我不想为其设置高度(因为它将根据其在网站上的位置而变化),里面有一个标题<div>,然后是一个应用了CSS的项目无序列表

它看起来很像这样:

Widget

我希望无序列表占据容器<div>中剩余的空间,知道标题<div>的高度为18px。我只是不知道如何将列表的高度指定为“100%减去18px的结果”。

我在SO上看到过这个问题在其他情况下被问到,但我认为为我的特殊情况再次提问是值得的。有人对这种情况有什么建议吗?


9
设置边距?__ - kennytm
@KennyTM,我猜你建议我在我的无序列表上加一个margin-top,比如说17px。但这只是把整个列表往下推;它并没有导致它缩小以适应容器。基本上,它的当前高度保持不变,但它只是被向下推了17像素。这并没有解决我的问题,但我认为这是朝着正确方向迈出的一步,因为我在网上看到过其他使用这种技术的方法。 - Matt
我刚刚解决了我在这里发布的问题。http://stackoverflow.com/a/10420387/340947 - Steven Lu
可能是CSS如何将div高度设置为100%减去n像素?的重复问题。 - 200_success
11个回答

1095
你可以使用 calc:
height: calc(100% - 18px);

请注意,一些旧浏览器不支持CSS3的calc()函数,因此可能需要实现供应商特定版本的该函数:

/* Firefox */
height: -moz-calc(100% - 18px);
/* WebKit */
height: -webkit-calc(100% - 18px);
/* Opera */
height: -o-calc(100% - 18px);
/* Standard */
height: calc(100% - 18px);

22
如果你使用Less,最好使用lessc --strict-math=on编译文件,以便Less不会评估calc内部的表达式——这是我在使用Less 2.0.0时遇到并花费了很多时间解决的问题。 - Dmitry Wojciechowski
63
注意减号周围的空格不是可选的:在我测试的浏览器中,100%-18px100%- 18px100% -18px都无法正常工作。 - nisetama
3
谢谢您的指出,这个解决方法对我也适用于LESS:4 ~'calc(50% - 20px)'; - sthzg
2
如果不起作用,请使用100vh而不是100% - maciejwww
请注意,在React中整个内容应该用引号括起来:height: 'calc(100% - 18px)' - undefined

74

如果你想采用一种不同的方法,你可以在列表上使用类似这样的方法:

position: absolute;
top: 18px;
bottom: 0px;
width: 100%;

只要父容器有position: relative;,这段代码就会生效。


1
对于像我一样的人,可能会感到奇怪:在子容器中,位置必须是“绝对的”,不能是“相对的”。 - Greg
1
继续(抱歉):然而,父元素只需要定位(也可以是absolute),并且它的高度也需要设置为100%。 - Greg
1
不幸的是,它在Safari iOS 8上无法工作。(已在Android 4.1原生浏览器和标准FF 34上测试通过):-( - Greg
这在某些情况下可能有效,但也可能会引起一些麻烦,因为绝对定位的元素从正常流中移除,参见https://dev59.com/WGcs5IYBdhLWcg3wfD5P#12821537。 - Christophe Weis
@ChristopheWeis 这是正确的,但在这种情况下这正是我们想要的 - 我们希望列表的大小由其父元素确定而不是相反。关键是要理解绝对定位的工作原理。https://www.w3.org/wiki/CSS_absolute_and_fixed_positioning - Nicolas Galler
我可以确认,使用position: absolute并设置bottom和top是目前最优雅的解决方案。令人惊讶的是,尽管在谷歌上搜索了数小时,大多数CSS教程网站甚至都没有提到这一点。相反,他们建议使用calc或flex容器。 - Leslie Krause

25

我用Jquery来完成这个操作

function setSizes() {
   var containerHeight = $("#listContainer").height();
   $("#myList").height(containerHeight - 18);
}

然后我将窗口调整大小绑定到重新计算它,以便每当浏览器窗口调整大小时都会进行重新计算(如果容器的大小随窗口调整而改变)

$(window).resize(function() { setSizes(); });

12
@puffpio,当然可以使用JS逐个解决这个问题。但是正如我所说,我正在尝试使CSS通用,适用于多个页面(顺便提一下,这些页面都继承自一个主页面)。因此,我不想使用JS。但还是谢谢你的回答。 - Matt
7
不要在 JavaScript 里面加入你自己的风格。 - Greg
8
@greg,如果CSS有更多有用的功能就会很有帮助。有些事情你必须使用hack方法来完成,这是荒谬的。 - Jonathan.
1
@puffpio 确实是个不错的解决方案。但是对我来说,在窗口上绑定事件并不是一个好的解决方案。如果我的页面有3-4个或更多这样的元素,我必须在窗口调整大小处理程序中更新每个元素的高度和宽度。这比CSS解决方案慢一些。 - Sachin Jain
@puffpio:不错的解决方案,但考虑将您的最后一行代码更改为$(window).resize(setSizes); 不需要使用一个单一方法调用的匿名方法。 - Tom G
1
将你的 xyz 放在 JavaScript 中就像是在说“不要支持 80% 的浏览器市场”。 - Beejor

15

不要将高度定义为百分比,只需将top=0bottom=0设置即可,如下所示:

#div {
   top: 0; bottom: 0;
   position: absolute;
   width: 100%;
}

你能解释一下为什么不能将高度设置为百分比吗? - sharat87
@ShrikantSharat 这是绝对定位元素的可视化格式规范的一部分。该解决方案利用了可能性5:“'height'为'auto','top'和'bottom'不为'auto',然后将'margin-top'和'margin-bottom'的'auto'值设置为0并解决'height'”。浏览器可以计算元素的高度,因为它知道离其父级顶部和底部的距离。 - Ross Allen
这似乎在Firefox中不起作用。即使所有父元素都设置为height:100%display:block。否则,这将是一个非常优雅的解决方案。有趣的是,即使水平居中正常工作,margin:auto也无法垂直居中固定宽度的对象。 - Beejor

8

假设标题高度为17像素

列出CSS:

height: 100%;
padding-top: 17px;

头部css:

height: 17px;
float: left;
width: 100%;

1
这并没有像我希望的那样好用。我的无序列表是在标题div下面开始的。标题div占据了容器中的空间,因此在列表上放置17px的padding-top只会将其从上图所示的位置向下推17px,而不是从容器div的顶部向下推17px。这是使用上面答案中给出的CSS得到的图片:http://i41.tinypic.com/mcuk1x.jpg。虽然我很感激你的努力,但如果你认为我漏掉了什么,请告诉我。顺便说一句,我的无序列表也有overflow: auto。 - Matt
2
如果您发布实际的CSS,我们就可以看到确切的交互情况,这样调试起来会容易得多。另外一个您可以尝试的方法是在ul上使用负上边距。ul { margin-top: -17px; } - ghoppe

7
  1. 使用负边距在你想减少像素的元素上。(目标元素)
  2. overflow:hidden;应用于包含元素。
  3. 将目标元素的overflow属性切换为auto

对我很有效!


这是一个非常优雅和兼容的解决方案。主要缺点是元素必须具有固定高度。否则,您需要在运行时使用 JS 或 calc() 将其居中。但对于许多情况来说,这不应该是问题。另一件需要记住的事情是,在调试代码时,使用大量负值的边距、填充和偏移量可能会导致混淆,因此请尽量保持简单。 - Beejor

5
尝试使用box-sizing属性。对于这个列表:
height: 100%;
/* Presuming 10px header height */
padding-top: 10px;
/* Firefox */
-moz-box-sizing: border-box;
/* WebKit */
-webkit-box-sizing: border-box;
/* Standard */
box-sizing: border-box;

对于页眉:

position: absolute;
left: 0;
top: 0;
height: 10px;

当然,父容器应该有类似于以下内容的东西:
position: relative;

3
另一种实现相同目标的方法是:使用弹性盒子(Flex Boxes)。 将容器设置为列式弹性盒子,然后您就可以自由地允许某些元素具有固定大小(默认行为),或者填充/收缩以适应容器空间(使用flex-grow:1和flex-shrink:1)。
#wrap {        
  display:flex;
  flex-direction:column;
}
.extendOrShrink {
  flex-shrink:1;
  flex-grow:1;
  overflow:auto;
}

请参见https://jsfiddle.net/2Lmodwxk/(尝试扩展或缩小窗口以注意效果)。
注意:您还可以使用简写属性:
   flex:1 1 auto;

我相信这可能是今天最好的答案,因为它意味着不必为标题div指定固定高度(如我原来的问题中的18px),也不必减去像填充和边距之类的东西。没有固定像素,一切都会自动处理。 - Matt

2
我尝试了其他的答案,但它们都不能完全满足我的需求。我们的情况非常相似,我们有一个窗口标题和可调整大小的窗口体中的图像。我们想要锁定调整大小的纵横比,而不需要添加计算来考虑标题的固定大小,并仍然使图像填充窗口体。
下面我创建了一个非常简单的代码片段,展示了我们最终采用的方法,这在我们的情况下似乎运行良好,并且应该在大多数浏览器上兼容。
在我们的窗口元素上,我们添加了一个20px的边距,这有助于相对于屏幕上的其他元素进行定位,但不会影响窗口的“大小”。然后将窗口标题绝对定位(这将其从其他元素的流中删除,因此它不会导致其他元素(如无序列表)被移动),并且其顶部定位为-20px,这将标题放在窗口的边距内。最后,将我们的ul元素添加到窗口中,高度可以设置为100%,这将导致它填充窗口的内容区域(不包括边距)。

*,*:before,*:after
{
  box-sizing: border-box;
}

.window
{
  position: relative;
  top: 20px;
  left: 50px;
  margin-top: 20px;
  width: 150px;
  height: 150px;
}

.window-header
{
  position: absolute;
  top: -20px;
  height: 20px;
  border: 2px solid black;
  width: 100%;
}

ul
{
  border: 5px dashed gray;
  height: 100%;
}
<div class="window">
  <div class="window-header">Hey this is a header</div>
  <ul>
    <li>Item 1</li>
    <li>Item 2</li>
    <li>Item 3</li>
    <li>Item 4</li>
    <li>Item 5</li>
  </ul>
</div>


1
感谢你的帮助,我已经解决了我的问题,稍微调整了一下,因为我想要一个宽度和高度都为100%的div(减去底部栏的高度),并且在body上没有滚动条(不需要hack /隐藏滚动条)。
对于CSS:
 html{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 body{
  position:relative;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }
 div.adjusted{
  position:absolute;width:auto;height:auto;left:0px;right:0px;top:0px;bottom:36px;margin:0px;border:0px;padding:0px;
 }
 div.the_bottom_bar{
  width:100%;height:31px;margin:0px;border:0px;padding:0px;
}

对于HTML:
<body>
<div class="adjusted">
 // My elements that go on dynamic size area
 <div class="the_bottom_bar">
  // My elements that goes on bottom bar (fixed heigh of 31 pixels)
 </div>  
</div>  

我成功了,我把div.adjusted的底部值设得比底部栏高度稍微大一点,这样就不会出现垂直滚动条了,我调整到最近的值。

这种差异是因为动态区域中的一个元素添加了一个额外的底部空洞,我不知道如何摆脱它...它是一个视频标签(HTML5),请注意我使用了以下CSS来放置该视频标签(所以没有理由它会产生底部空洞,但它确实产生了):

 video{
  width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

目标:拥有一个视频,它占据浏览器的100%(并且在浏览器调整大小时动态调整大小,但不改变纵横比),除了底部空间,我用它来放置一些文本、按钮等(当然还要符合w3c和css的验证)。
编辑:我找到了原因,视频标签就像文本一样,并不是块级元素,所以我用以下CSS进行修复:
 video{
  display:block;width:100%;height:100%;margin:0px;border:0px;padding:0px;
 }

请注意视频标签上的 display:block;

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