I am attempting to create a layout using Flexbox. In one of these layouts, I want 3 equal width columns. To accomplish this I am using calc to set the column width. This is working fine in modern browsers, but of course in IE it doesn't want to work. Here is my code:
.container {
width:50vw;
margin:0 auto;
display:flex;
}
.container > div {
flex:1 0 calc(100% / 3);
}
<div class="container">
<div>Test</div>
<div>Test</div>
<div>Test</div>
</div>
As I mentioned, this works fine in modern browsers, but in IE the columns just collapse on each other unless I use a specific percentage in place of calc.
It's a known bug.
IE 10-11 ignore calc() functions used in flex shorthand declarations.
Since this bug only affects the flex shorthand declaration in IE 11, an easy workaround (if you only need to support IE 11) is to always specify each flexibility property individually.
source: https://github.com/philipwalton/flexbugs#flexbug-8
So, in other words, instead of:
flex: 1 0 calc(100% / 3)
Try:
flex-grow: 1;
flex-shrink: 0;
flex-basis: calc(100% / 3);
Also, consider this: You don't even need the calc() function.
If you want three equal width columns, this will do:
flex: 1
or
flex: 1 0 30%
or even:
flex: 1 0 26%;
With flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis to be 33.33%.
Since flex-grow will consume free space on the line, flex-basis only needs to be large enough to enforce a wrap (should it become necessary).
In this case, with flex-basis: 26%, there's plenty of space for the margins, borders, padding, etc., but never enough space for a fourth item.
Related
So, to wrap elements in a flex div using a row layout all I have to do is this:
div {
display: flex;
flex-direction: row; /* I want to change this to column */
flex-wrap: wrap; /* wrap doesn't seem to work on column, only row */
}
<div>
<p>these</p>
<p>will</p>
<p>wrap</p>
</div>
This works for my rows, but I want to make it work for my columns as well.
I tried just changing flex-direction to column, but it doesn't seem to be working. Does anyone know how to get this functionality?
Block-level elements, by default, take up the full width of their containing block. This, in effect, resolves to width: 100%, which sets a break in the flow of content in the horizontal direction.
So, flex items can wrap by default in a row-direction container.
Nothing in HTML or CSS, however, sets a default height on block-level elements. Heights are content-driven (height: auto).
This means that elements will flow vertically without having any reason to break.
(I guess somewhere along the line of evolution, possibly on the basis of usability studies, it was decided that it would be okay for web applications to expand vertically, but not horizontally.)
That's why flexbox doesn't automatically wrap items in column direction. It requires an author-defined height to serve as a breaking point.
Often times, however, a layout's height is dynamic so a specific height cannot be set. That makes flexbox unusable for wrapping items in column direction. A great alternative is CSS Grid Layout, which doesn't require a height setting on the container:
div {
display: grid;
grid-gap: 10px;
}
p:nth-child(3n + 1) {
grid-row: 1;
background-color: aqua;
}
p:nth-child(3n + 2) {
grid-row: 2;
background-color: orange;
}
p:nth-child(3n + 3) {
grid-row: 3;
background-color: lightgreen;
}
p {
margin: 0;
padding: 10px;
}
<div>
<p>ONE</p>
<p>TWO</p>
<p>THREE</p>
<p>FOUR</p>
<p>FIVE</p>
<p>SIX</p>
<p>SEVEN</p>
<p>EIGHT</p>
<p>NINE</p>
</div>
Browser Support for CSS Grid
Chrome - full support as of March 8, 2017 (version 57)
Firefox - full support as of March 6, 2017 (version 52)
Safari - full support as of March 26, 2017 (version 10.1)
Edge - full support as of October 16, 2017 (version 16)
IE11 - no support for current spec; supports obsolete version
Here's the complete picture: http://caniuse.com/#search=grid
I want to make a layout that satisfies the following conditions:
1) it has a block on the top whose height is up to its content
2) below it has a code-mirror and a block side by side, which fill in exactly the rest of the page in terms of height.
I have made a plunker here. The problem is it works well in Chrome 57.0.2987.133, whereas it does NOT work well in Safari 10.1: the height of the code-mirror is NOT enough; it shows only 76 lines of the code rather than the correct 80 lines.
Does anyone know how to fix this?
<style>
.rb {
display: flex;
flex-direction: column;
height: 100%;
}
.rb .container {
flex: 1;
display: flex;
width: 100%;
height: 100% /* new */
}
.rb .first-row {
border: 1px solid black;
/*flex: 0 0 60px;*/
}
.rb .CodeMirror {
flex: 1;
height: auto;
}
.rb .flex-preview {
flex: 1;
border: 1px solid black;
}
</style>
<div class="rb">
<div class="first-row">
1<br/>2<br/>3<br/>4<br/>
</div>
<div class="container">
<textarea ng-model="body" ui-codemirror="option"></textarea>
<div class="flex-preview">
</div>
</div>
</div>
<body>
<div ng-app="myApp" ng-controller="myCtrl">
<ui-view></ui-view>
</div>
<script>
var app = angular.module("myApp", ['ui.router', 'ui.codemirror']);
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('global', {
templateUrl: 'template.html'
})
}]);
app.controller('myCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.option = { mode: 'text/html', lineNumbers: true, matchBrackets: true };
$scope.body = ""
for (var i = 1; i <= 79; i++)
$scope.body = $scope.body + "a\n";
$state.go('global')
}])
</script>
</body>
.rb .container {
flex: 1;
display: flex;
width: 100%;
height: auto /* new */
}
Try using height as 'auto'.
According to http://www.caniuse.com, there are few known issues with Safari re vh:
Safari & iOS Safari (both 6 and 7) does not support viewport units
for border widths, column gaps, transform values, box shadows or in calc().
iOS 7 Safari sets viewport unit values to 0 if the page has been left
and is returned to after 60 seconds.
iOS 7 Safari recalculates widths set in vh as vw, and heights set in vw
as vh, when orientation changes.
vh on iOS is reported to include the height of the bottom toolbar in the
height calculation, and the width of the sidebar (bookmarks) in the vw
width calculation.
As you have set border-widths in the code posted, the use of vertical height (vh) as a measurement unit is going to pose a problem for you.
There are a few workarounds I suppose, you could use percentage, adjust the vh if safari using browser detection (modernizer?) or you could perhaps add a margin or padding? Just some thoughts off the top of my head. Good luck.
Hope this helps
EDIT: Your issue may lie with the use of flex which unfortunately has issues in Safari which is marked as fixed because it used to appear in Chrome also, but was fixed in Chrome 51. It still occurs in Safari according to caniuse
In Safari, the height of (non flex) children are not recognized in percentages.
However other browsers recognize and scale the children based on percentage heights. (See bug)
The bug also appeared in Chrome but was fixed in Chrome 51
The indication that non-flex children is NOT measured in % would suggest that vh may be used instead (which leads back to my earlier answer).
On the upside, it's not just you that's experiencing the issue! It's a known bug that should be fixed in a future release. Every cloud has a silver lining.. :)
EDIT #2
Another issue with Safari (iOS) is that it doesn't support min-width (actually only on table elements on second look) in CSS. Your externally linked in stylesheets may be using min-width so this may also have an effect on the output. It is recorded as happening in iOS 5.1 but it is unclear whether it was fixed in later versions.
You're setting the height of the code-mirror container (.rb) using height: 100%. But you don't have any height specified on the parent container.
When you use percentage heights, some browsers still require you to define a height on the parent (using the height property). That's been the traditional implementation of the spec language, although not all browsers adhere to that interpretation anymore.
However, if you're going to use height: 100% on an element, and you want to ensure cross-browser support, make sure the parent container has a defined height. If the parent also uses a percentage height, then its parent must also have a defined height.
Alternatively, just use height: 100vh on .rb and be done with it.
Then your flex: 1 on .container will work to consume available height.
More information and other solutions here:
Chrome / Safari not filling 100% height of flex parent
Working with the CSS height property and percentage values
As we all know, the flex property is a shorthand for the flex-grow, flex-shrink, and the flex-basis properties. Its default value is 0 1 auto, which means
flex-grow: 0;
flex-shrink: 1;
flex-basis: auto;
but I've noticed, in many places flex: 1 is used. Is it shorthand for 1 1 auto or 1 0 auto? I can't understand what it means and I get nothing when I google.
flex: 1 means the following:
flex-grow : 1; ➜ The div will grow in same proportion as the window-size
flex-shrink : 1; ➜ The div will shrink in same proportion as the window-size
flex-basis : 0; ➜ The div does not have a starting value as such and will
take up screen as per the screen size available for
e.g:- if 3 divs are in the wrapper then each div will take 33%.
Here is the explanation:
https://www.w3.org/TR/css-flexbox-1/#flex-common
flex: <positive-number>
Equivalent to flex: <positive-number> 1 0. Makes the flex item flexible and sets the flex basis to zero, resulting in an item that
receives the specified proportion of the free space in the flex
container. If all items in the flex container use this pattern, their
sizes will be proportional to the specified flex factor.
Therefore flex:1 is equivalent to flex: 1 1 0
BE CAREFUL
In some browsers:
flex:1; does not equal flex:1 1 0;
flex:1; = flex:1 1 0n; (where n is a length unit).
flex-grow: A number specifying how much the item will grow relative to the rest of the flexible items.
flex-shrink A number specifying how much the item will shrink relative to the rest of the flexible items
flex-basis The length of the item. Legal values: "auto", "inherit", or a number followed by "%", "px", "em" or any other length unit.
The key point here is that flex-basis requires a length unit.
In Chrome for example flex:1 and flex:1 1 0 produce different results. In most circumstances it may appear that flex:1 1 0; is working but let's examine what really happens:
EXAMPLE
Flex basis is ignored and only flex-grow and flex-shrink are applied.
flex:1 1 0; = flex:1 1; = flex:1;
This may at first glance appear ok however if the applied unit of the container is nested; expect the unexpected!
Try this example in CHROME
.Wrap{
padding:10px;
background: #333;
}
.Flex110x, .Flex1, .Flex110, .Wrap {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
}
.Flex110 {
-webkit-flex: 1 1 0;
flex: 1 1 0;
}
.Flex1 {
-webkit-flex: 1;
flex: 1;
}
.Flex110x{
-webkit-flex: 1 1 0%;
flex: 1 1 0%;
}
FLEX 1 1 0
<div class="Wrap">
<div class="Flex110">
<input type="submit" name="test1" value="TEST 1">
</div>
</div>
FLEX 1
<div class="Wrap">
<div class="Flex1">
<input type="submit" name="test2" value="TEST 2">
</div>
</div>
FLEX 1 1 0%
<div class="Wrap">
<div class="Flex110x">
<input type="submit" name="test3" value="TEST 3">
</div>
</div>
UPDATE 2021
The latest versions of all major browsers appear to implement flex: 1 and conform to W3C standard. This was verified on Chrome, Opera, Edge, Firefox, Safari, Chromium and a few Chromium variants like Brave on macOS, Windows, Linux, iOS, and Android. When attempting to test in Internet Explorer the Edge browser was force-loaded on Windows 10.
If you expect
users to still implement older versions of browser then adding units is a safer bet.
I was also confused with flex: 1, so I will share here my way of understanding this property :)
To understand the concept of flex: 1, first we have to make sure the parent element has a display property set to flex i.e., display: flex. Now, the nested flexed elements inside the parent can make use of flex: 1.
Ok now the question is what will this do to the flexed element? If an element has flex: 1, this means the size of all of the other elements will have the same width as their content, but the element with flex: 1 will have the remaining full space given to it. See the illustration below.
In Chrome Ver 84, flex: 1 is equivalent to flex: 1 1 0%. The followings are a bunch of screenshots.
The default values are set to 1 1 0% (which are the shorthand values for flex-grow flex-shrink flex-basis respectively) probably because these are the values that make the most sense for "flex" items. Here are what exactly those values mean:
flex-basis: It specifies the ideal size for the items. Ideal means "assuming there is neither any extra space, nor any shortages of the space". 0% means we have no ideal size for them, we want them to be sized truely flexibly. We want them to be sized automatically(thus the word "flexible") based on the available space.
flex-grow: After taking the flex-basis into consideration, if there's remaining extra space, it specifies how "that extra space"(notice we're not talking about the whole space) must be divided between the items. The ones with higher flex-grow will eat up more of the extra space. It makes sense to use an equal flex-grow on all items by default so that all items will have the same share of the extra space. When flex-basis is 0%, a flex-grow of 1 on all items makes them divide "the whole space of the container"(since flex-basis used no space, the extra space equals the whole space of the container).
flex-shrink: After taking the flex-basis into consideration, if the available space is not enough, it specifies how "the shortage of space"(and again, not the whole space) must be divided(imposed on) among the items. The ones with higher flex-shrink will have to "endure" more of that shortage.
Examples:
flex-basis A flex-basis of 400px on 3 items, means that we'd rather have 3 items of 400px wide each. Now, what will happen:
If we have extra space? Let's say the container width is 1500 pixels wide. The 3 items will take up 1200 pixels, what should happen to that extra 300 pixels?
If we have shortage of space in the container? E.g., If there are 5 items of 400 pixels each in a 1500px container (shortage = |1500px - 5 * 400px| = 500px).
The answer to the two questions above are flex-grow(answer to the 1st question) and flex-shrink(answer to the 2nd question).
E.g., what if one of the three items had a flex-grow of 5 and the other ones were still on their default values(i.e., 1)? Then the one with the flex-grow of 5 would get (300px / (1+1+5)) * 3 of the extra space.
Another useful example is, if you have a flex container and you want each of the children to take exactly the full width of the parent(e.g., an image carousel), in that case you may use a flex: 0 0 100% on all children so that items will have a flex-basis of taking the full-width of the parent, and turning their growing/shrinking off.
flex: 1 sets flex-grow to 1 (whereas the default value is 0).
What this does:
If all items have flex-grow set to 1, the remaining space in the
container will be distributed equally to all children. If one of the
children has a value of 2, the remaining space would take up twice as
much space as the others (or it will try to, at least).
(Source: CSS Tricks)
In the case of a one-value flex syntax with a unitless number (rather than a value for flex-basis, the other option),
...it is interpreted as flex: <number> 1 0; the
flex-shrink value is assumed to be 1 and the flex-basis value is
assumed to be 0.
(Source: MDN)
Flex: 1 is equivalent to Flex: 1 0 0 and Flex: 1 1 0
Please see the images I took showing the output, respectively, for Flex: 1 and Flex: 1 0 0 and Flex: 1 1 0 below
Flex: 1
Flex: 1 0 0
Flex: 1 1 0
This question already has answers here:
Chrome / Safari not filling 100% height of flex parent
(5 answers)
Why don't flex items shrink past content size?
(5 answers)
Closed 5 years ago.
In the following questions, I've been able to get all the cases to work out, I'm just looking to debug my mental model. I'm also only concerned with Chrome, if that makes answering easier.
I have an overflow:auto within nested "holy grail-ish" flexbox layouts. The overflow:auto behavior works fine for 2-level and 3-level nesting.
However, once I get to 4-level nesting, it "breaks," requiring me to specify the min-height:0 property (despite my having consistently specified flex-basis:0 via flex:1, which should annul the flex-basis:content/content-sized default). Why is this only happening at 4-level nesting?
Also, the element I need to slap the min-height:0 onto is .orange. Why this element, and why not the other ancestors?
Can anyone explain the above two questions? I have been consulting the spec and am having trouble connecting its rules back to my 4-level-deep example.
Note that this is different from the other questions I've been able to find on SO regarding flexbox and overflow, for instance (see in particular my answers):
overflow: auto in nested flexboxes
Nested flexbox with scrolling area
I have an overflow:auto within nested "holy grail-ish" flexbox layouts. The overflow:auto behavior works fine for 2-level and 3-level nesting.
Your 2-level code does indeed work as intended in Chrome, and IE11. However, it fails in Firefox. Same thing with your 3-level code: Works in Chrome and IE11, but not Firefox.
However, once I get to 4-level nesting, it "breaks," requiring me to specify the min-height:0 property (despite my having consistently specified flex-basis:0 via flex:1, which should annul the flex-basis:content/content-sized
default). Why is this only happening at 4-level nesting?
Once again, your statement is true for Chrome and IE11, but not for Firefox.
Solutions
Let's start with the fixes, so that all demos work in Chrome, Firefox and IE11. (I didn't test in Safari, but that's WebKit like Chrome, so it should be fine with vendor prefixes for any versions prior to 9.)
Also, I'll use compiled code in the answer, as not everybody uses preprocessors.
Revised 2-level (added two lines of code)
.violet {
flex: 1;
background: violet;
display: flex;
flex-direction: column;
min-height: 0; /* new */
min-width: 0; /* new */
}
Revised 3-level (added four lines of code)
.violet {
flex: 1;
background: violet;
display: flex;
flex-direction: column;
min-height: 0; /* new */
min-width: 0; /* new */
}
.orange {
flex: 1;
background: orange;
display: flex;
min-height: 0; /* new */
min-width: 0; /* new */
}
Revised 4-level (added one line of code)
.violet {
flex: 1;
background: violet;
display: flex;
flex-direction: column;
/* For some reason this is not needed */
/* min-height:0; */
min-width: 0; /* new */
}
Breaking Down the Behavior
There's a lot going on with your nesting. I'm not going to debug the code line-by-line, but I'll offer three concepts that may be useful to you.
1. Calculating Percentage Heights
Chrome, Firefox and IE11 can have different interpretations for an element's height.
Here's what it says in the spec:
CSS height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly and this element is not absolutely positioned, the value computes to "auto".
auto The height depends on the values of other properties.
Traditionally, when calculating percentage heights, browsers have interpreted the spec's use of the term "height" to mean the value of the height property.
Based on a reading of the height definition, the interpretation could just as easily be the computed height, but the height property requirement has become the predominant implementation. I've never seen min-height or max-height work on a parent when dealing with percentage heights.
Chrome expects to see the height property when calculating height. If it doesn't, it computes the height to auto. Firefox, however, has a broader interpretation of the spec. It accepts flex heights, as well (as evidenced here and here and here).
It's not clear which browsers are more compliant.
It doesn't help matters that the height property definition hasn't been updated since 1998 (CSS2).
In all three of your demos you're combining percentage heights, pixel heights and flex heights. You may want to keep the differing browser interpretations in mind when troubleshooting your code.
Here are some more details: Working with the CSS height property and percentage values
2. Why doesn't flex item shrink past content size?
3. flex-basis: 0 vs flex-basis: auto
flex: 1 1 auto (or flex: auto, for short), sizes a flex item based on the content size or height properties.
flex: 1 1 0 (or flex: 1, for short), sizes a flex item based on the free space in the flex container.
Each may have a different effect on the behavior of overflow: auto.
More details here: Page-filling flexbox layout with top and side bars not quite working
Try to imagine a situation
there is a div and inside the container there are three divs ,
and sometimes we need to let the inside divs self-adaption.
like this
css:
.a{display:-webkit-box;width:300px;height:100px;background:#222}
.a div{-webkit-box-flex:1;height:100px}
.a-1{background:red}
.a-2{background:yellow}
.a-3{background:blue}
html:
<div class="a">
<div class="a-1">abc</div>
<div class="a-2">abcdddd</div>
<div class="a-3">abcdddddddde</div>
</div>
but a-1 ,a-2 , a-3 do not self-adaption .i mean a-1 a-2 a-3 do not equal in length. it seems also depends on the text length.
how solve?
Looks like you have misunderstood the purpose of the flexible box layout. It works by taking the unused space in the containing element and adding into its children. So for example if your containing box is 300px, and you have three elements originally 80px, 100px, and 60px, then you have 300-80-100-60 = 60px. Then if your three child elements all have a flex value of 1 then it allocates 60/(3*1) = 20px to each. So the child sizes are now 100px, 120px, and 80px.
For your example, as you want them equal sizes, you should make the -webkit-box-flex to 0 for all three children, but set their width (or height if appropriate) to 33.33% each.
I have occasions where I do not know the number of elements that will be within an element, but I still want them to be layed out evenly. For instance if I had 3 elements their width would be 33%, but for 4 elements the width would be 25%. To get around this I set the width to 1% first, then set flex to 1.
.flex-container {
display: -webkit-box;
display: -moz-box;
display: -ms-box;
display: box;
}
.flex-element {
-webkit-box-flex: 1;
-moz-box-flex: 1;
-ms-box-flex: 1;
box-flex: 1;
width: 1%;
}
I think you need to add display: -webkit-box to .a div{-webkit-box-flex:1;height:100px}