为一个元素设置长度(高度或宽度)减去另一个元素的可变长度,即 calc(x - y),其中 y 未知

Setting a length (height or width) for one element minus the variable length of another, i.e. calc(x - y), where y is unknown

我知道我们可以在定义长度时使用 calc:

flex-basis: calc(33.33% - 60px);
left: calc(50% - 25px);
height: calc(100em/5);

但是如果长度是可变的呢?

height: calc(100% - <<header with variable height>>);

width: calc(100% - 50px - <<box with variable width>>);

在 CSS 中是否有执行此操作的标准方法?

我知道使用 flexbox 和表格可以完成整个任务,但我想知道 CSS 是否提供了更简单的方法。 Flexbox、表格和简单 Javascript 是可接受的替代方案。

height demo

width demo

Flexbox 可以做到这一点。

支持 IE10 及更高版本。

JSfiddle Demo

* {
  margin: 0;
  padding: 0;
}
html,
body {
  height: 100%;
}
#container {
  height: 100%;
  display: flex;
  flex-direction: column;
}
#top {
  background-color: lightgreen;
}
#bottom {
  background-color: lightblue;
  flex: 1;
}
<div id="container">
  <div id="top">green box variable height</div>
  <div id="bottom">blue box no longer overflows browser window</div>
</div>

您可以使用 CSS 个表格:

.wrapper {
  display: table;
  width: 100%;
  margin: 15px 0;
}

.horizontal.wrapper > div {
  display: table-cell;
  white-space: nowrap; /* Prevent line wrapping */
  border: 1px solid;
}
.left { width: 100px } /* Minimum width of 100px */
.center { width: 0; }  /* Width given by contents */

.vertical.wrapper { height: 200px; }
.vertical.wrapper > div {
  display: table-row;
}
.vertical.wrapper > div > span {
  display: table-cell;
  border: 1px solid;
}
.top    { height: 100px; } /* Minimum heigth of 100px */
.middle { height: 0; }     /* Height given by content */
.bottom { height: 100%; }  /* As tall as possible */
<div class="horizontal wrapper">
  <div class="left">100px wide</div>
  <div class="center">Auto width, given by contents</div>
  <div class="right">Remaining space</div>
</div>
<div class="vertical wrapper">
  <div class="top"><span>100px tall</span></div>
  <div class="middle"><span>Auto height, given by contents</span></div>
  <div class="bottom"><span>Remaining space</span></div>
</div>

水平大小写也可以用float实现:

#wrapper, .right { overflow: hidden; } /* Establish BFC */
#wrapper > div { border: 1px solid; }
.left, .middle { float: left; }
.left { width: 100px }
<div id="wrapper">
  <div class="left">100px</div>
  <div class="middle">Auto width, given by contents</div>
  <div class="right">Remaining space</div>
</div>

已更新

正如我 earlier, and besides flex, this is also solvable using display: table and here is a fiddle demo我展示的那样。

如果垂直演示也需要固定顶部,这里是我原来的 display:table 版本的更新:fiddle demo

有时我不能(或不想)使用 flex 或 tables,我断断续续地研究过使用 css calc() and css attr().

两者都有不足之处,因为 calc() 只能使用 +-*/attr() 只能 return 一个字符串值,无法通过 calc().

我的建议,使用普通 javascript,是基于这两种方法在某些时候可能会被扩展,以便我们更好地利用它们。

这就是我希望看到它们的工作方式;

width: calc(100% - attr(this.style.left))

但他们没有,我也不能将它添加到我的 css 中,因为它无法正确验证(甚至可能破坏解析,谁知道)我添加了一个变体作为取而代之的是元素上的属性,并带有一些怪癖以使其更易于计算。

在这种情况下(2 个演示)看起来像这样:

//height
<div id="bottom" data-calcattr="top,height,calc(100% - toppx)">...</div>

//width 
<div class="box right" data-calcattr="left,width,calc(100% - leftpx)">...</div>

连同下面的脚本,它绝不是所有 属性 组合的完全 developed/tested,它确实调整了 div 的大小。

简而言之,运行时,它获取属性,将其拆分为一个数组,将第一个项目值作为属性读取的第一个项目值,第二个项目属性设置和读取值获得 inserted/replaced 并分配给要设置的 属性 的第三个(嗯,仍在研究更好的方式来表达这一点,但希望脚本足够清楚,发生了什么) .

这里是 a fiddle 显示高度和宽度的演示,集成,使用相同的脚本。

function calcattr() {
    var els = document.querySelectorAll('[data-calcattr]');
    for (i = 0; i < els.length; i++) {
        var what = els[i].getAttribute('data-calcattr');
        if (what) {
            what = what.split(',');
            var rect = els[i].getBoundingClientRect();
            var parentrect = els[i].parentNode.getBoundingClientRect();
            var brd = window.getComputedStyle(els[i].parentNode,null).getPropertyValue('border-' + what[0] + '-width');            
            what[2] = what[2].replace(what[0],parseInt(rect[what[0]]-parentrect[what[0]]) - parseInt(brd));
            els[i].setAttribute("style", what[1] + ":" + what[2]);
        }
    }
}

I'm looking for something simple and portable. In the same way a CSS property can be easily applied across documents, I'm looking for something similar in terms of ease-of-application for this function.

... isolated fix is preferred.

水平:

这只能使用 CSS 来实现。由于您不喜欢 flex 布局解决方案,下一个最佳选择是 table 布局。

一个简单的 CSS 片段,您可以将其放入您的项目(并完成),如下所示:

div.flexh {
    display: table; box-sizing: border-box; padding: 0; margin: 0;
}
div.flexh > div { 
    display: table-cell; width: auto; 
    box-sizing: border-box; vertical-align: middle;
}
div.flexh > div:first-child {
    /* Override your custom styling below */
    min-width: 75px; width: 75px; max-width: 75px; 
}
div.flexh > div:last-child { width: 100%; }

然后您可以根据站点要求将 site-specific 样式添加到此基础 CSS。比如,nowrap

两个apparent这个方案的优点是:

  1. 您不需要更改标记,也不需要用 class 装饰所有 children。只需将 class flexh 应用到您的 parent div 即可。

需要最少的标记:

<div class="flexh">
    <div>...</div>
    <div>...</div>
    <div>...</div>
</div>
  1. 您不仅限于三列。您可以根据需要拥有尽可能多的列。第一个将具有固定宽度,最后一个将是灵活的,所有列 in-between 将获得 content-based 宽度。

演示 Fiddle:http://jsfiddle.net/abhitalks/qqq4mq23/

演示片段:

div.flexh {
    display: table; box-sizing: border-box; padding: 0; margin: 0;
    /* Override your custom styling below */
    width: 80%; border: 2px solid black;
    border-right: 2px dashed black; 
    font-size: 1em;
}
div.flexh > div { 
    display: table-cell; width: auto; 
    box-sizing: border-box; vertical-align: middle;
    /* Override your custom styling below */
    background-color: lightgreen; border: 1px solid #ddd;
    padding: 15px 5px;
}
div.flexh > div:first-child {
    /* Override your custom styling below */
    min-width: 75px; width: 75px; max-width: 75px; 
    background-color: orange;
}
div.flexh > div:last-child {
    width: 100%;
    /* Override your custom styling below */
    background: skyblue;
}
<div class="flexh">
    <div>75px Fixed Width</div>
    <div>Variable Content Width</div>
    <div>Flexible Remaining Width</div>
</div>

<hr/>
<div class="flexh">
    <div>75px Fixed Width</div>
    <div><img src='//placehold.it/128x48/66c' /></div>
    <div>Flexible Remaining Width</div>
</div>

<hr/>
<div class="flexh">
    <div>75px Fixed Width</div>
    <div>Variable TextWidth</div>
    <div>
        <img src='//placehold.it/128x48/66c' />
        <p>Variable ContentWidth</p>
    </div>
    <div>Flexible Remaining Width</div>
</div>


垂直:

如果没有 flex 布局,实现起来有点棘手。 table 布局在这里不起作用 主要是因为 table-row 不会按照您的要求保持固定高度use-case。 table-rowtable-cell 上的 height 仅表示所需的最小高度。如果 space 被限制,或者内容超出可用 space,那么 cellrow 将根据内容增加其高度。

根据此处的规格:http://www.w3.org/TR/CSS21/tables.html#height-layout

The height of a 'table-row' element's box is calculated once the user agent has all the cells in the row available: it is the maximum of the row's computed 'height', the computed 'height' of each cell in the row, and the minimum height (MIN) required by the cells...

...the height of a cell box is the minimum height required by the content

这个效果可以在这里看到:http://jsfiddle.net/abhitalks/6eropud3/

(调整window窗格的大小,你会看到第一行的高度会增加,因为内容无法适应指定的高度,因此违背了目的)

因此,您可以使用内部标记(如 div 元素)间接限制高度,或者放弃 table-layout 并计算灵活元素的高度。在您的 use-case 中,您不想更改标记,因此我不建议使用内部标记。

这里的best-bet就是使用普通blockdiv的time-tested模型,灵活计算高度。正如您已经发现 CSS 无法实现的那样,您将需要一个小的 JavaScript 片段来为您做到这一点。

一个简单的 JavaScript 片段( 没有 jQuery),您可以将其包装在 window.load 中并放入您的项目中(并且完成)看起来像这样:

var flexv = document.querySelectorAll('div.flexv');
/* iterate the instances on your page */    
[].forEach.call(flexv, function(div) {
    var children = [].slice.call(div.children), // get all children
        flexChild = children.splice(-1, 1),     // get the last child
        usedHeight = 0, totalHeight = div.offsetHeight;

    children.forEach(function(elem) {
        usedHeight += elem.offsetHeight; // aggregate the height
    });
    /* assign the calculated height on the last child */
    flexChild[0].style.height = (totalHeight - usedHeight) + 'px';
});

CSS 片段或多或少类似于水平片段,没有 table 布局,您也可以将其放入您的项目并添加额外的 site-specific 样式。所需的最少标记保持不变。

演示 Fiddle 2: http://jsfiddle.net/abhitalks/Ltcuxdwf/

演示片段:

document.addEventListener("load", flexit);

function flexit(e) {
 var flexv = document.querySelectorAll('div.flexv');
 [].forEach.call(flexv, function(div) {
  var children = [].slice.call(div.children), 
   flexChild = children.splice(-1, 1), 
   usedHeight = 0, totalHeight = div.offsetHeight;
  children.forEach(function(elem) {
   usedHeight += elem.offsetHeight;
  });
  flexChild[0].style.height = (totalHeight - usedHeight) + 'px';
 });
}
div.flexv {
    display: inline-table; box-sizing: border-box; padding: 0; margin: 0; 
    overflow: hidden;
    /* Override your custom styling below */
    height: 320px; width: 20%; border: 1px solid black; font-size: 1em;
    margin: 8px;
}
div.flexv > div { 
    display: block; height: auto; box-sizing: border-box; 
    overflow: hidden;
    /* Override your custom styling below */
    background-color: lightgreen; border: 1px solid #ddd;
    padding: 5px 15px;
}
div.flexv > div:first-child {
    /* Override your custom styling below */
    min-height: 36px; height: 36px; max-height: 36px; 
    background-color: orange;
}
div.flexv > div:last-child {
    height: 100%;
    /* Override your custom styling below */
    background: skyblue;
}
<div class="flexv">
    <div>36px Fixed Height</div>
    <div>Variable Content Height</div>
    <div>Flexible Remaining Height</div>
</div>

<div class="flexv">
    <div>36px Fixed Height</div>
    <div><img src='//placehold.it/64x72/66c' /></div>
    <div>Flexible Remaining Height</div>
</div>

<div class="flexv">
    <div>36px Fixed Height</div>
    <div>Variable Text Height</div>
    <div>
        <img src='//placehold.it/72x48/66c' />
        <p>Variable Content Height</p>
    </div>
    <div>Flexible Remaining Height</div>
</div>

注意:正如@LGSon 所指出的,用于演示的 display: inline-table 在 Firefox 中运行不佳。这仅用于演示,应根据您的 use-case.

替换为 blockinline-block

输入CSS

虽然我从未尝试过,但我相信这会奏效:

.top {
    height:13px;
}

.main {
    height:calc(100% - var(height));
}

http://www.creativebloq.com/netmag/why-you-need-use-css-variables-91412904


输入SASS

$top_height: 50px

.main {
    height: calc(100% - $top_height)
}

Sass Variable in CSS calc() function

在容器 css 的两种情况下,您应该放置:

#container {
    overflow: hidden;
}

但是,它会隐藏溢出容器的信息。我认为这就是重点,因为你把 white-space: nowrap; 这意味着你不想改变高度,所以你必须隐藏不适合容器的文本。