Flexbox change item style when wrapped - html

Is it possible to apply styles once an item wraps?
I have a 2 column flexbox where the first item sticks to the left and the one on the right should be in the center of the remaining space. The solution is to add margin: auto to the second item.
// Not relevant for the question - toggle container width
document.querySelector('button').addEventListener('click', () => {
const flexbox = document.querySelector('#flexbox');
if (flexbox.style.maxWidth) {
flexbox.style.maxWidth = null;
} else {
flexbox.style.maxWidth = '300px';
}
});
#flexbox {
display: flex;
flex-wrap: wrap;
gap: 20px;
border: 2px solid #397298;
}
#flexbox>div {
background: #8bc6e3;
padding: 20px;
}
#item2 {
margin: auto;
}
<div id="flexbox">
<div id="item1">Flex item 1 with quite some content</div>
<div id="item2">Flex item 2</div>
</div>
<button style="margin-top: 30px">Toggle shrink container</button>
Now the question is, how can I remove the margin: auto of #item2 if it's wrapped?
I want it to be on the left side and not centred.
This is what I want:
Not wrapped:
Wrapped:
There were multiple other instances where I wanted to change the style of the flexbox items when they wrap.
I find it very hard sometimes to write general CSS rules on more complex responsive designs. Did a lot of fiddling with grid as well, but it seems that there is no option to customize the wrapped items, even though it changes the page layout and item arrangement quite a lot.
I'm looking forward to container queries that go in this direction, but it's not yet supported by all browsers.
Edit: the button is not question relevant
The items wrap because of a narrow screen size, not because a user makes an action that could be caught by JS.

If you want to do this with pure css, you only choice is to use mediaqueries afaik. Usually you want to wrap the items when the app is used on phone or some other small device. You can also control the behavior in a more deterministic manner if you give the flex-items a basic width, so you are sure when they'll wrap and when not.
Let's say you want to have 2 rows of some fixed size content:
then you can give both
flex: 0 0 50%
This will make the flex-basis 50% of the parents width.
now with a media control the wrapped state
#media(max-width 600px) { margin: none; }
I guess there is no way to achieve this without js in the first place, quoting a stackoverflow fellow from this post:
In CSS, once the browser renders the page on the initial cascade, it
doesn't reflow the document when an element wraps. As a result, parent
elements don't know when their children wrap.

Related

4 sibling elements with variable heights: how to float/flex 3+4 to right, w/o vertical gaps between 1+2 or 3+4?

I am attempting to turn a CMS-generated page of four sibling elements stacked atop one another into a two-column layout. Here is the simplified generated markup:
<section id="element-container">
<header id="element-1">Text header</header>
<header id="element-2">Text subheader</header>
<div id="element-3">Text validation messages</div>
<form id="element-4">Form fields go here</form>
</section>
I need a left column with #element-1 and #element-2 stacked together, and a right column with #element-3 and #element-4 stacked together.
I cannot change the CMS-generated markup (such as to add more nesting levels of container elements).
In real-world usage, #element-4 will almost always be considerably longer than the total combined height of #element-1 and #element-2, though every so often we need to dump a bunch of text into #element-2, making it taller than #element-4.
A CSS-only solution is called for over something like jQuery manipulation of elements via detach() or wrap() etc, for the sake of avoiding an ugly flash of content being rearranged; and hiding everything until it's manipulated is very much undesirable as well. For our particular application, fast/clean loading is the absolute top priority. However, I'd be able to do some of that type of manipulation to (only) the #element-3 div, as it contains feedback to the user about a failed form validation, so it has display:none and height:0px at page load. So I'd be very happy with a solution that "only" stacks #element-1 and #element-2 directly atop one another at left/top, and #element-4 at right/top—from there I can find a way to deal with #element-3.
I spent all day today playing with solutions like the one in Floating 3rd element right while first 2 go left, which is so close to what I need, but all solutions to questions like these seem to assume fixed heights on the elements, and break when uncertainty is introduced to heights. For example, if you play with the codepen on the accepted answer on that question by making div3 200px high instead of 100px high, you get a big gap between div1 and div2.
Open to float, flexbox, whatever. Thanks very much.
You can pretty easily force a two-column layout by using column-count on your container element. This will make the browser automatically divide up the elements into however many columns you specify based on their heights, without leaving vertical space between them.
In your case, with two columns, this will basically automatically ensure #element-1 is on the left and #element-4 is on the right. The question becomes where do #element-2 and #element-3 fall in the auto layout. If the first two elements are pretty small, element 3 may end up in the left column; or if the first element is quite tall and elements 2, 3, and 4 are small enough, they may end up all together in the right column.
Sounds from your description like we probably don't have to worry too much about element 2 ending up in the right column, so I'll focus on forcing element 3 to the right column. (If the former is a problem, the easiest solution is a min-height on element 2, but you can experiment with different options.)
Since element 3 is display: none to start, one option you can do is to figure out how tall it should be right before you display it, and then absolute position it at the top of the right column while moving element 4 down by the same amount as element 3's height to prevent overlap. I'm using a simple top margin on element 4 in the example below.
In the example below, the height of element 3 is static, so the logic is simple enough, but in your real case you may have to do something like:
run form validation
add error messages to element 3
set element 3 to display: block; visibility: hidden
get the height of element 3
offset element 4 by element 3's height
set element 3 to visibility: visible
document.getElementById('element-4').style.marginTop = `${document.getElementById('element-3').getBoundingClientRect().height}px`;
body {
margin: 0;
}
* {
box-sizing: border-box;
}
#element-container {
column-count: 2;
column-gap: 0px;
position: relative;
}
#element-1, #element-2, #element-4 {
display: inline-block;
width: 100%;
}
#element-3 {
display: block;
}
#element-1 {
background-color: firebrick;
padding: 40px 10px;
}
#element-2 {
background-color: midnightblue;
color: white;
padding: 30px 10px;
}
#element-3 {
background-color: gold;
padding: 10px;
position: absolute;
top: 0;
right: 0;
width: 50%;
}
#element-4 {
background-color: mediumaquamarine;
padding: 160px 10px;
}
<section id="element-container">
<header id="element-1">Text header</header>
<header id="element-2">Text subheader</header>
<div id="element-3">Text validation messages</div>
<form id="element-4">Form fields go here</form>
</section>

How do I allow overflow on a CSS Grid row whose height is set to `auto`

I am trying to implement a simple view.
This view is comprised of a top view and a bottom view.
The top is static and uninteresting.
The bottom will contain content that will overflow that component's boundaries. In the event of this, I want it to show a scroll overflow.
This is roughly how I want it to look (gif screen recording)
The issue is that I'm in a situation where they layout is built in CSS grid. Don't ask why, this is not up for change. Also using viewport units are off the table.
If you read the code below, the layout roughly translates to:
"Create two rows. The first row will have a defined height. The second row will fill in the rest of the space available"
While this works when the content of the bottom row is small, it breaks when the content of the bottom row overflows. Specifically, it expands the size of .bottomRow and the component goes off screen without overflowing.
Here is a screen recording that illustrates this behavior
Whats happening here is that the blue div you see is taking on a height to match the overflowing size of its children. I want it to preserve its size, and overflow instead.
Here's my code so far (this is not exactly the code from the screen recordings. Just the essentials)
.layout {
width: 100%;
height: 100%;
display: grid;
grid-template: max-content auto / 1fr
}
.topRow {
height: 50px;
width: 100%
}
.bottomRow {
width: 100%;
height: 100%;
/* max-height: ???; works if set to a static value */
box-sizing: border-box;
border: 5px solid black;
overflow-y: auto;
}
<div class='layout'>
<div class='topRow'/>
<div class='bottomRow'>
// ENOUGH CONTENT TO OVERFLOW
</div>
</div>
The only way I've been able to successfully implement this is by setting a max-height.
See screen recording
But to make this dynamic, I would have to add an event listener for component resizing. Not ideal!
I've also tried using max-height: -webkit-fill-available, and while this worked on Chrome, it doesn't work against other major browsers.
Please help! Overall, I'm trying to find a solution that avoids 3rd party libraries, JavaScript, event listeners, and is cross browser compatible.
I had the same issue today and setting the grid-template-rows to a value of max-content 1fr did the job.
CSS needs a fixed value for doing a scrolling behaviour. auto does not count as such, buuut 1fr does on the other hand!
Michelle

Continue div element past horizontal overflow

A JSFiddle of it: http://jsfiddle.net/24tL8mkq/3/
I want the red highlighting to continue all the way across the box.
Right now, it's set-up such that:
<div style='width: 500px; overflow: auto; border: 1px solid black; padding-top:-5px;'>
<pre id='pre_1'>
<!-- code box -->
</pre>
</div>
with the relevant css (this is the CSS that I want to extend across the entire div, through the overflow) being:
.bad {
background-color: palevioletred;
width: 100%;
}
I get that I can't use width: 100% as that'll only extend to the right most side of the overflow always, but I can't set a static width as I don't know what the size of the box could be.
I'd really prefer to keep this a HTML/CSS solution if possible just to make this as portable as possible.
Interesting problem. The following works for me in the latest Firefox, Chrome and IE11, though I'd consider this somewhat "experimental" - definitely should be further tested if you need to support a broader range of browsers.
http://jsfiddle.net/24tL8mkq/5/
pre {
display: table;
}
pre > div { display: flex; }
I wish I could tell you why this works, but I don't know. I wasn't able to find another combination that works, however. My guess: setting the pre to display: table makes it so the width will go wider than 100% (500px), as tables will do (when their children are wider than the table). Setting flex on the div children is filling the available space since all the children should be equal width.

How to distribute column widths efficiently based in the width of child elements?

I've been given a design to create in HTML/CSS that I think may be impossible, but I want to be absolutely sure before I admit defeat and go with a compromise.
A number of items containing variable length text are arranged into sets of two such that they form columns where the columns are distributed across the available width exactly, but ensuring each column is no wider than its widest item. Thereby it uses horizontal space as efficiently as possible while still ensuring items align perfectly as a grid. Consider this example -
[-a--] [-cccccc-] [-eee-] [-g---]
[-bb-] [-dddd---] [-ff--] [-hhh-]
Perhaps this looks tabular, but consider that the items must also wrap when the container shrinks, and still the columns maintain their magical alignment -
[-a---] [-cccccc-]
[-bb--] [-dddd---]
[-eee-] [-g------]
[-ff--] [-hhh----]
I can't work out how to resolve the recursive dependency of widths. That is that the width of each "column" is defined by the longest item in that column, but the total available width is known and must be an exact sum of the columns widths.
I'm happy to use a CSS3 only solution, falling back to a fixed item width solution for older browsers, but I am completely stumped as to what CSS properties can achieve this magic.
Just to provide some kind of starting point, here's a Codepen using CSS3 columns: http://codepen.io/anon/pen/kiGgp
It's no good as the columns are distributed evenly, and I'm wondering if there's a way to distribute them optimally instead.
I didn't manage to achieve what you specified even after numerous tries with the FlexBox model. (BTW: this model is currently poorly supported by browsers)
But, in my efforts, I came with a decent solution, maybe you'll like it too.
Demo in this Fiddle
I've manage to evenly distribute the space between the elements in each row, while allowing element wrapping.
each element get the exact amount of space he needs, and the rest is divided as spaces within the row.
but, this width remain even when elements wrap. so you don't have the 'Column like' display.
this is pure CSS, and cross Browser.
Tested on: IE10, IE9, IE8, Chrome, FF
HTML
<div class="wrap">
<div class="pair">
<p>a</p>
<p>b</p>
</div>
<div class="pair">
<p>cccccccc</p>
<p>dddddd</p>
</div>
<div class="pair">
<p>ee</p>
<p>f</p>
</div>
<div class="pair">
<p>ggggggggggg</p>
<p>hhhhhhhhh</p>
</div>
<div class="pair">
<p>iiii</p>
<p>jjjjjj</p>
</div>
<div class="filler"></div>
</div>
CSS
*
{
padding: 0;
margin: 0;
}
.wrap
{
background-color: #ccc;
line-height: 0;
text-align: justify;
}
.pair
{
line-height: normal;
background-color: #ddd;
display: inline-block;
width: auto;
}
.filler
{
width: 100%;
height: 0;
font-size: 0;
display: inline-block;
vertical-align: top;
}

HTML/CSS: 3 columns, variable sides and fixed centered middle column

My problem is with the header. So I basically have 3 columns of divs. I want the middle one to have a constant width of 980px, and then I want the left of the header to extend to the end of the browser window with a blue background color. As for the right of the header, I want that to extend to the end of right side of the browser with a black background color. It kind off looks like this:
<------------------------------[blue][center header][black]---------------------------->
I've done my research and all I could find so far are two columns with a fixed left column with the right column filling up the rest of the space. I wonder how this can be applied to my problem?
Would it be like:
<div style="width:100%;">
<div style="display:table-cell; background-color:blue;"></div>
<div style="width: 980px;">my header</div>
<div style="display:table-cell; background-color:black;"></div>
</div>
Thank you!
A simple solution - basicaly using your exact stying, but putting another block in the central table-cell element, something like this span here:
<div class="wrapper">
<div class="left"></div>
<div class="center"><span>my header</span></div>
<div class="right"></div>
</div>
I moved all the inline style to a separate CSS block and used class selectors:
.wrapper {
display:table;
width:100%;
}
.left {
display:table-cell;
width:50%;
background-color:blue;
}
.right {
display:table-cell;
width:50%;
background-color:black;
}
.center {
display:table-cell;
}
.center span {
display:inline-block;
width:900px;
}
here is a jsfiddle
and here I made the center much narrower for a better illustration: jsfiddle
Hope this helps =)
Unfortunately there isn't a super smooth way of doing this that is also has wide cross compatibility support. There is a CSS spec for display called flex or flexbox which would do what you want beautifully and elegantly, but it has very limited support at the moment. Here is some resources on flexbox for your perusal...
http://css-tricks.com/old-flexbox-and-new-flexbox/
In the meantime, you can achieve the layout you want with some basic CSS jiggery-pokery that will get you what you want, but it requires absolute positioning your middle div.
Heres the JSFiddle: http://jsfiddle.net/CW5dW/
Here's the CSS:
.left {
width: 50%;
height: 300px;
float: left;
padding-right: 160px;
box-sizing: border-box;
background: red;
}
.right {
width: 50%;
height: 300px;
float: right;
padding-left: 160px;
box-sizing: border-box;
background: blue;
}
.middle {
position: absolute;
width: 300px;
height: 300px;
left: 50%;
padding: 10px;
margin-left: -150px;
box-sizing: border-box;
background: orange;
}
What is going on here you might ask?
Basically, we are taking the div with class middle and removing it from the flow of the document. This allows us to float our left div left, and our right div right, with widths of 50% in order to fluidly take up ALL space of the browser.
We then tell the middle div to take up 300px of space (in your case 980), and we tell it to go 50% of the total width of your browser from the left. This doesn't center it though, because its calculated from the left edge of your div. So we give it a negative margin space of half it's width, to sort of "move" that left edge to the center of the div.
Then, since we know the middle div has a width of 300px (in your case 980), we can then say that the left div should have some padding on its right edge greater than or equal to half the middle divs width, in my example that's 150px, and I added 10px more so text couldn't come right to the edge of the div, so 160px total. We do the same for the right div but for it's left side. This limits the content of those two divs from falling underneath our middle div.
This answer is not an "answer" as such - it's an extended comment to #Michael's post. I have, however, posted another answer - a jQuery solution.
Regarding #Michael's answer (which is a very tidy solution indeed) there is a tiny issue that if you remove your height declaration (which the OP undoubtedly will) then the backgrounds for the various columns become exposed - this method relies on the backgrounds all levelling out at their bottom edge in order to make the design coherent. If the OP's design doesn't have backgrounds behind the columns then this solution should be fine. If backgrounds are required (which they might be judging by the question wording) then it could be awkward. Two solutions to this...
a simple javascript that scans the page for column length, finds the longest, and matches all shorter ones to the maximum.
The other (and probably better) solution is to drop a background into your with the columns already on it (it only needs to be 1px high I guess) - just make sure the central white band is 980px wide and the side columns extend off a thousand or so pixels to accommodate even the largest of browsers
OK, here's my solution. This will present a "common or garden" three column fixed width layout to all users and then adjust it for users with javascript enabled (which, let's face it, is the vast majority of users). The benefits of this solution are that the layout will behave like any ordinary 3 solumn layout without the quirks you can experience from using more advanced CSS tweaks like absolute positioning and fixed heights.
Fiddle here... http://jsfiddle.net/vuary/
You should be able to see what's going on with the HTML and CSS... it's basic stuff. The jQuery is pretty straight forward too:
$(document).ready(function(){
// find the width of the browser window....
var docuWidth = $(window).width();
// find the width of the central column as set by the CSS...
// (you could hard code this as 980px if desired)
var centerWidth = $('#center').width();
// figure out how many pixels wide each side column should be...
sideColWidth = (docuWidth-centerWidth) / 2;
// then set the width of the side columns...
$('#left,#right').css({
width:sideColWidth+'px'
});
})
EDIT
Converted the jQuery to a function that is called when the document is ready, and again if the viewport is resized... just in case:
http://jsfiddle.net/aKeqf/