I am having a chrome specific issue with removing elements from a flexbox item from a height specified flexbox container. This is causing strange display issues for another scrollable flexbox item within that container. It seems that the scrollable item is not properly refresh/repainting, while it is growing.
This is a fairly specific question that's easier to see in the example.
https://jsfiddle.net/gajv6snz/
Note: This is not specific to removing the elements using javascript. Manually deleting them in DevTools causes this also.
<div id="container">
<div id="box1"></div>
<div id="box2"></div>
</div>
#container {
height: 300px;
display: flex;
flex-direction: column;
}
/* Removing elements from #box1 causes display issues with box2 */
#box2 {
overflow-y: scroll;
}
I expect that bottom scrollable flexbox item to take over the full container once the top flexbox item has been emptied. I want the container to remain the same size throughout.
Related
I have a layout where I have a scrollable list of items in the center, with some stuff above and below it in a column. The list should take up as much empty space as is available to it in the column (I don't want to specify a specific height on it), and should scroll when the empty space in the column is not enough to fit all the items.
Here is a JSFiddle of the situation.
Notice that with just a few items in the scroller, it expands to fill the empty space in the column (as is intended). But then if you add several more items to the scroller, it expands to fit the entirety of its contents instead of staying at its original size, and then scrolling its overflowing contents; even though the it has overflow-y: scroll set!
However, if you then set the scroller to have a height of 0, the problem is fixed and the items scroll as is intended, with the scroller at its original height before the extra items were added.
But WHY!? Can someone please explain to me what's going on here? Also, is there any consequences of this "solution" that I'm not seeing?
<div class="column">
<div class="title">Header</div>
<div class="scroller">
<div class="item">Child</div>
<div class="item">Child</div>
</div>
<div class="title">Footer</div>
</div>
,
.column {
display: flex;
flex-direction: column;
height: 200px;
}
.title {
height: 50px;
flex-shrink: 0;
}
.scroller {
flex-grow: 20;
flex-shrink: 0;
overflow-y: scroll;
}
.item {
height: 20px;
margin-top: 2px;
}
Some quick background for anyone who runs across this later:
Elements that have flex-grow expand to take up x units of the available space minus the other flex content. In your case, .scroller is the only one with flex-grow but the other flex elements have defined heights so their content takes up space.
Elements that have flex-shrink contract as the space decreases. A zero value means they don't contract, a value >=1 allows scaling down.
However flex-shrink ONLY works if the element DOES NOT also have a flex-grow applied to it. Elements with both shrink & grow will only shrink to the size of their content
In your example, overflow doesn't kick in when the element is as big as the content (see above) which it is because you have both grow/shrink applied. Adding an explicit height (height: 0) overrides the computed "content" height allowing the flex-shrink to compress the element smaller than its content. This, in turn, makes the scrollbar work.
I don't know if this will cause any oddities at some point but it's an interesting solution to the problem and does seem to work pretty well.
You need to add a height to your scroller container css:
height: 30px;
Or max-height to allow growth to a limit:
max-height: 30px;
Either way, for the scroll to kick in, the container needs to feel constrained, so maybe:
height: 100%;
Then limit the size of its container.
I was able to get this working by setting flex-shrink on the scroller element to 1. It will cap out at its available space and use the scrollbar.
Can someone let me know if this is an undocumented bug with flexbox, or that I'm just doing it wrong? I've got 3 images lined up in a row inside a div container. This is as simple as it gets folks.
Without any hyperlinks, all 3 images shrink down perfectly as they should.
<div style="width: 100%; margin: 0 auto; display: flex; justify-content: center;">
<img src="flash-tooltip.png">
<img src="html-tooltip.png">
<img src="portables-tooltip.png">
</div>
Now, only 2 out of the 3 images when viewed on all devices shrink down correctly depending on manually maximizing dragging the browser, of via viewport.
The only image that will not change shape or size is the image with the hyperlink. So, I took the hyperlink off the first image. And decided to test it by placing it on the 2nd, now the 1st image and the 3rd image shrinks fine.
But, the 2nd image stays the exact same size? Tried then adding hyperlinks to all the images and none of them change to match the screen width?
Am I wrong to say flex items if they are images won't flex if they have a hyperlink lol? Surely this cannot be the case right?
The problem has nothing to do with hyperlinks. You could wrap the image in any element (try a span or a div) and it will have the same effect as the a container.
The problem is the hierarchical structure of a flex container.
When you set an element to display: flex (or inline-flex) you establish a flex container.
Only the children of a flex container are flex items. Descendants of a flex container beyond the children are not flex items and don't accept flex properties.
Here are the three flex items:
<img src="flash-tooltip.png">
<img src="html-tooltip.png">
<img src="portables-tooltip.png">
The img in the first element is not a flex item. It is wrapped by the a element and is therefore a child of a flex item.
The two img items can shrink because of two default settings on a flex container:
flex-wrap: nowrap ~ flex items are forced to remain on a single line
flex-shrink: 1 ~ flex items are permitted to shrink to prevent them from overflowing the container
If you switch to flex-wrap: wrap and/or flex-shrink: 0 the img items will no longer shrink.
The a item does not shrink because of another default setting: min-width: auto, which means that flex items cannot be smaller than the size of their content. In this case, the a item cannot shrink below the width of the image.
You can override this setting by adding min-width: 0 to your code.
#container {
display: flex;
}
span {
min-width: 0;
display: flex;
}
img {
min-width: 0;
}
<div id="container">
<span><img src="http://i.imgur.com/60PVLis.png"></span>
<img src="http://i.imgur.com/60PVLis.png">
<img src="http://i.imgur.com/60PVLis.png">
</div>
More information:
Why don't flex items shrink past content size?
Proper use of flex properties when nesting flex containers
I don't know why, but this solves the problem. I would like to know why as I cannot find out any information about this issue in any HTML/CSS documents.
If you add the following.
<style>
img {
max-width: 100%;
height: auto;
}
</style>
Then all 3 images will shink perfectly. Even if they have hyperlinks. Funny enough if you set just the width: 100%; then the image with the hyperlink stays the exact same size as the image is, and all the others without hyperlinks blow up to the 100% size of the container.
I didn't know flexbox had such rules that needed you to set image max-widths to make items responsive/shrink down if they have a hyperlink attached.
So, tried it in chrome: Only the image now with the anchor shrinks down, the other 2 stay the same size. FireFox all 3 shrink down, but chrome only shrinks the image with the hyperlink wrapped around it.
Tried wrapping hyperlinks around each of the other 2 images and in chrome, they all shrink down fine.
Can someone explain what is going on? How can i set a max-width: and height: auto on a hyperlink?
It's tough to say without seeing your CSS, but you probably are not selecting the images within <a> tags. If you alter your CSS to select images that are inside of <a> tags, it should work fine.
I've added my complete working solution. Thanks to many people here giving their various methods. So, this is for anyone else who may be struggling.
First lets set the style's up.
<style>
img {
max-width: 100%;
width: 100%;
min-width: 0;
min-height: 0;
}
</style>
adding min-width: 0; | min-height: 0; seems to be overkill, but with chrome, it works much better apparently than setting them as auto;
Since it's using flexbox we don't add the usual width: 33.33%; even if there are 3 images. In flexbox, this will just space them out way to far apart within a 100% wide div.
Here's the really important part I found out the hard way.
You must use either width: 100% on the images, or max-width: 100%; otherwise, (On Chrome without adding either 100% width or max-width: 100%; it just won't flex/shrink down when you minimize the browser to test its responsiveness.)
So, next to keep each of the 3 images in perfect aspect ratio remember to include each image inside its own div container. Otherwise, they will shrink but will just skew up to each other as they do.
As you can see the first image is even wrapped in a hyperlink, but because it's inside its own div it will shrink and grow completely flush and inline with the other images. This saves using extra markup and saves adding a span tag then making that a flex container to contain the hyperlink. I've tried both ways this is by far the easier method.
I've used inline styles for the flexbox container. (bad habit.)
<div style="border: 2px solid red; margin: 0 auto; display: flex; justify-content: center;">
<div>
<img src="flash-tooltip.png">
</div>
<div>
<img src="html-tooltip.png">
</div>
<div>
<img src="portables-tooltip.png">
</div>
</div>
Remember to close off that last /div it's a real gotcha!
And that's how I've done it. Tested it in many browsers works perfectly. Even on mobile phones and tablets.
If you don't like flexbox? You can do the same thing using regular floats.
I've included this same method as above, only this time in a float: version.
This question already has answers here:
Can't scroll to top of flex item that is overflowing container
(12 answers)
Closed 6 years ago.
In this example, as soon as the browser window height drops below 400px, the image is no longer centered in the scrollable area.
html {
height: 100%;
}
body {
height: 100%;
display: flex;
align-items: center;
margin: 0;
padding: 0;
}
#content {
height: 400px;
display: flex;
align-items: center;
}
<div id="content">
<img src="http://placehold.it/300x300">
</div>
It works as soon as I unset the height property of the html or of the body or of both.
Still, I want to understand why centering in this specific example fails. Does it have something to do with the nested flexboxes? Or is there something problematic with setting the height of both, html and body, to 100%? Is it a bug or something browser related?
It's similar to margin: 0 auto (in conjunction with position: relative) for horizontal centering: As long as the container is wider than the centered child, the child will be centered. But as soon as the container (or the body/viewport) is narrower than the child, the child will be aligned left and a scrollbar will appear. That way it will always possible to see the whole child element - since it's larger than the container, that only is possible when scrolling is enabled.
In the situation you describe, the same happens with flexbox and vertical centering: If the parent container would be smaller (i.e. less high) than the child, and the child would be centered without a scrollbar, you wouldn't be able to see it's top and bottom part (which can be important for example if it's a form where you have to fill in text fields etc.), and you wouldn't be able to scroll to see those hidden parts. So in this situation (child higher than parent with child vertically centered in flex container), the child will be put at the top of the parent and you will be able to scroll down, which is good to either read/see the whole content or see/fill in the whole form in case of forms.
I've reordered some elements in my html using flexbox in the responsive design of a website which works fine but the elements then won't resize properly.
At a breakpoint I have applied a class of flex to the home-promos div and reordered the elements. This works correctly.
The problem then arises when I try to resize the div's to percentage widths. They will only resize up to a certain point, such as 50% and then won't get any bigger.
Is anyone who is better with flexbox than myself able to tell me what the issue is?
.home-promos {
display: flex;
}
.home-promo-center {
order: 1;
}
.home-promo-left {
order: 2;
}
.home-promo-right {
order: 3;
}
<div class="home-promos">
<div class="home-promo-left">
<div class="promo-left-content">
*content*
</div>
</div>
<div class="home-promo-center">
<div class="promo-center-content">
*content*
</div>
</div>
<div class="home-promo-right">
<div class="promo-right-content">
*content*
</div>
</div>
</div>
When you create a flex container (display: flex or display: inline-flex), it comes with several default settings. Among them are:
flex-direction: row - flex items will align horizontally
justify-content: flex-start - flex items will stack at the start of the line
flex-wrap: nowrap - flex items are forced to stay in a single line
flex-shrink: 1 - flex items are allowed to shrink
Note the last two settings.
Your three divs are forced to remain in a single line. Hence, their combined width is limited to the width of the container.
Also, they are allowed to shrink, which prevents them from overflowing the container. This also limits their width.
To apply whatever width you want to each flex item you can override these initial settings with:
flex-wrap: wrap - now there's more space because flex items can break to new lines
flex-shrink: 0 - now there's more space because flex items will not shrink and can overflow their container if necessary
When using css flexbox the three main browsers appear to behave entirely differently in certain areas.
In this case I am trying to create a grid of images:
<div class="container">
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
</div>
.container {
display:inline-flex;
flex-flow : column wrap;
align-content : flex-start;
height : 100%;
}
In this example I need a container, itself containing several div elements set up to flow from top to bottom and wrapping when they reach the bottom. Ultimately providing me with columns of photos.
However I need the container to expand horizontally to accommodate the wrapped elements:
Here is a quick jsFiddle to demonstrate.
The behaviour is as follows:
IE 11 - Correct, the container stretches horizontally to wrap each column of wrapped elements
Firefox - The container only wraps the first column of elements, with the rest overflow out.
Chrome - The container always stretches to fill the width of its parent, whatever that may be.
In this instance I would like to achieve the behaviour of IE11 in the other two browsers. Therefore my question is, how can I make a flexbox container expand horizontally to match its column wrap contents.
Thanks in advance.
It's curious that most browsers haven't implemented column flex containers correctly, but the support for writing modes is reasonably good.
Therefore, you can use a row flex container with a vertical writing mode. This will swap the block direction with the inline direction, and thus the flex items will flow vertically. Then you only need to restore the horizontal writing mode inside the flex items.
.container {
display: inline-flex;
writing-mode: vertical-lr;
flex-wrap: wrap;
align-content: flex-start;
height: 350px;
background: blue;
}
.photo {
writing-mode: horizontal-tb;
width: 150px;
height: 100px;
background: red;
margin: 2px;
}
<div class="container">
<div class="photo">1</div>
<div class="photo">2</div>
<div class="photo">3</div>
<div class="photo">4</div>
<div class="photo">5</div>
<div class="photo">6</div>
<div class="photo">7</div>
<div class="photo">8</div>
<div class="photo">9</div>
</div>
This approach may have its own bugs in edge cases, especially if you mix advanced layout techniques like floats and nested flexboxs. But for most cases it seems to work properly.
The spec says that what you're doing should work, but it's implemented incorrectly in every major browser besides Internet Explorer / Edge, making multi-line inline-flex column layouts useless at present for most developers. Here's a Chromium bug report providing an example that is effectively identical to yours, and noting that it renders incorrectly in Chrome, Safari, and Firefox.
The argument from spec is more complicated than I'm able to understand, but the key point is that Flexible Box Layout Module Level 1 spec defines the intrinsic cross-size of a flex container (that is, the intrinsic height of a flex-direction: row flex container or the intrinsic width of a flex-direction: column flex container) in the section Flex Container Intrinsic Cross Size. There, it is stated:
For a multi-line flex container, the min-content/max-content cross size is the sum of the flex line cross sizes
That is, the intrinsic width of a flex-direction: column flex container should be the sum of the widths of its columns, as you'd expect. (There is more complexity than this, and I don't understand it all, but I believe the above to be broadly true.) However, Chrome, Firefox, and Safari all calculate this width incorrectly; setting width: min-content or width: max-content on a column wrap flex box in Chrome, you can clearly see that the width is set to the width of the widest single element.
A silly Chrome-specific workaround exists, but is probably best avoided. Until the bug is fixed, this part of the Flexbox model simply doesn't work as designed and there's no clean solution available.
It seems this issue cannot be solved only with CSS, so I propose you a JQuery solution
container width = position of the last child - position of the container + width of the last child (including margin)
Code :
$(document).ready(function() {
$('.container').each(function( index ) {
var lastChild = $(this).children().last();
var newWidth = lastChild.position().left - $(this).position().left + lastChild.outerWidth(true);
$(this).width(newWidth);
})
});
Demo :
http://jsfiddle.net/qzea320L/
You have a column layout distribution with a fixed height container.
When you set the flex-direction to column you define the Vertical axis as the main axis.
In flexbox that means it will fill up the available height and then create a new column.
In this JSBIN I use javascript to change the container's height and, because of that, you will see the child items move.
PS: you shouldn't rely on IE behavior since their flex support is recent.
Another possible approach:
.container {
column-count: 2; /*or whatever */
}
.container > div {
display: inline-block;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/column-count
You may also need to adjust margin-top of .container > div:first-child if they don't align to the top.