Flex-wrap with equal width - javascript

I use flex-wrap in CSS. I want a layout that accept these conditions:
Use flex-wrap.
Use the same width of all the boxes/child elements.
Use the width of the largest child element. No fixed widths.
It's not required to fill the width of the container.
In the jsfiddle it would probably be 1 or 2 items on each row.
Is it possible with some clever css flex solution or do I need to use javascript?
https://jsfiddle.net/swysdq22/
.wrap {
width: 300px;
background: #fff;
}
.narrow {
display: flex;
flex-wrap: wrap;
}
.narrow > * {
background: #ccc;
border: 2px solid #fff;
padding: 10px;
color: #fff;
}
<div class="wrap">
<div class="narrow">
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
</div>
</div>

You can use map() and get() to return array of widths and then with Math.max you can find maximum value in that array and apply it as width on each item
var w = $('.narrow > div').map(function() {
return $(this).width();
}).get();
var max = Math.max.apply(null, w);
$('.narrow > div').width(max);
.wrap {
width: 300px;
background: #fff;
}
.narrow {
display: flex;
flex-wrap: wrap;
}
.narrow >* {
background: #ccc;
border: 2px solid #fff;
padding: 10px;
color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrap">
<div class="narrow">
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
<div class="item1">
Longest sentence.
</div>
<div class="item2">
Short text.
</div>
<div class="item3">
Another text.
</div>
</div>
</div>

Related

How to center CSS Grid but child elements at the start?

I want to implement this layout with CSS Grid or Flexbox. If necessary, it is also possible to use JS.
In the pictures you can see how the child elements should be arranged when adding 1, 2, 3 and 4 child elements in a sequence
The content is generated dynamically. TThat is, the number of child elements is unknown. There may be a maximum of 3 child elements in a row. The grid items should be centered. The whole Layout should also be responsive.
.parent {
background-color: red;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 20px;
justify-content: center;
/* 3* 350px width of child elements + 2*20px gap*/
max-width: 1090px;
padding: 20px;
}
.child {
width: 350px;
height: 450px;
background-color: blue;
}
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
It is currently not responsive (if possible without media queries) and the child elements are not centered.
Do you know how I can implement this layout?
Don't define 3 columns but rely on implicit column creation:
.parent {
background-color: red;
display: grid;
grid-auto-columns: calc((100% - 2*20px)/3); /* size of one column */
grid-gap: 20px;
justify-content: center;
margin: 10px;
}
.child {
height: 50px;
background-color: blue;
}
/* place each item in the adequate column */
.child:nth-child(3n + 1) {grid-column: 1}
.child:nth-child(3n + 2) {grid-column: 2}
.child:nth-child(3n + 3) {grid-column: 3}
<div class="parent">
<div class="child"></div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
<div class="parent">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>
I think I have something for this using implicit grids:
* {
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
gap: 50px;
background: #000;
}
section {
width: 100%;
padding: 30px;
background: #666;
}
.grid-inner {
display: grid;
gap: 30px;
justify-content: center;
grid-auto-columns: calc(33% - 15px);
}
.box {
padding: 30px;
background: white;
border: 1px solid black;
}
.box:nth-child(2) {
grid-column: 2;
}
.box:nth-child(3) {
grid-column: 3;
}
<section>
<div class="grid-inner">
<div class="box"></div>
</div>
</section>
<section>
<div class="grid-inner">
<div class="box"></div>
<div class="box"></div>
</div>
</section>
<section>
<div class="grid-inner">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</section>
<section>
<div class="grid-inner">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</section>
<section>
<div class="grid-inner">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
</section>

How to align boxes right under each other with flexbox

The point is that I want to implement this layout with html, css and js.
When you click on the button, the container should alternately become taller/smaller.
picture 1
picture 2
However, no fixed height should be passed, as is currently implemented.
Without using max-height, scaleY.
Also, text5 should be right under text2 and text6 right under text3 (as you can see in the picture) without using width or something similar.
The flex items should only be as large as the content.
I've also searched for solutions, but the search results weren't what I was looking for.
How can I transition height: 0; to height: auto; using CSS?
Flexbox not giving equal width to elements
Align divs under each other
let isOpen = false;
function toggleHeight() {
let row2 = document.getElementById('row2');
if(!isOpen){
isOpen= !isOpen;
row2.style.height = '120px';
//row2.style.height = 'auto';
}
else{
isOpen= !isOpen;
row2.style.height = '0';
}
}
.row1, .row2 {
display: flex;
gap: 10px;
background-color: #ccc;
}
.row2 {
height: 0;
transition: 0.25s ease;
overflow: hidden;
}
.col1 {
display:flex;
}
<div onclick="toggleHeight()">Click Me</div>
<div class="row1">
<div class="col1">
<div>
<div>text</div>
<div>text</div>
</div>
<div>
<div>text</div>
<div>text</div>
</div>
<div>
<div>text</div>
<div>text</div>
</div>
</div>
<div class="col2">
<div>text2</div>
<div>text2</div>
</div>
<div class="col2">
<div>text3</div>
<div>text3</div>
</div>
</div>
<div id="row2" class="row2">
<div class="col1">
<div>text4</div>
<div>text4</div>
</div>
<div class="col2">
<div>text5</div>
<div>text5</div>
</div>
<div class="col2">
<div>text6</div>
<div>text6</div>
</div>
</div>
I created exactly you wanted but the transition on height is not working!
const button = document.querySelector('button')
const row2 = document.querySelector('#row-2')
button.addEventListener('click', () => {
row2.classList.toggle('visible')
})
#import "https://cdn.jsdelivr.net/gh/KunalTanwar/normalize/css/normalize.inter.min.css";
body {
height: 100%;
display: grid;
place-items: center;
}
button {
padding: 4px;
border: 1px solid #c1c1c1;
}
.flex {
margin-top: 32px;
display: flex;
flex-direction: column;
}
.flex .row {
display: inherit;
column-gap: 16px;
}
.flex .row .col {
display: inherit;
padding-inline: 16px;
flex-direction: column;
}
.flex .row#row-2 {
height: 0px;
overflow: hidden;
background-color: red;
transition: height 250ms ease, overflow 250ms ease;
}
.flex .row#row-2.visible {
height: auto;
overflow: visible;
}
<div>
<button>Click Me!</button>
<div class="flex">
<div class="row" id="row-1">
<div class="col">
<span>texttexttext</span>
<span>texttexttext</span>
</div>
<div class="col">
<span>text2</span>
<span>text2</span>
</div>
<div class="col">
<span>text3</span>
<span>text3</span>
</div>
</div>
<div class="row" id="row-2">
<div class="col" style="align-self: center;">texttexttext</div>
<div class="col">
<span>text5</span>
<span>text5</span>
</div>
<div class="col">
<span>text6</span>
<span>text6</span>
</div>
</div>
</div>
</div>
P.S - remove padding-inline from .col.

How do I toggle visibility of an entire dynamic row (flex wrap)?

Current Behavior
I have the following basic structure:
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<!-- ... dozens of .boxes ... -->
</section>
#containers is .display: flex; flex-wrap: wrap, so the number of boxes on any one row is dynamic. This is an important feature that must be maintained.
Here's a minimal working example:
$(document).ready(function() {
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
});
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0.5em;
margin-left: 16px;
display: none; /* Initially collapsed */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content">Content</div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content">Content</div>
</div>
</section>
</body>
I can easily use .slideToggle() to toggle visibility of a sibling (.content) underneath one of the clickable .collapsible divs:
$(".collapsible").click(function() {
$( this ).next().slideToggle();
});
Desired Behavior and Remarks
What I'd like is, on click of any .collapsible div in a row, the entire row will be toggled. That is, every .content div on the same horizontal row as displayed in the current viewport.
This must handle rows with dynamic number of columns, including viewport resizing. I'm flexible on the precise behavior, though.
Is this possible? And how much will it hurt?🙂 Changes to the document structure (such as adding some sort of row container) are OK, and I don't mind using some JS/jQuery code of course. The main thing I can't do is hard code the number of columns, as I need my site to remain responsive on vertical phones and fullscreen desktop browsers.
Maybe the jQuery slideToggle() function is not the best method.
Since a row will be the same height , you may look at a method to set size from 0 to another value. anybox resized will stretch other boxes on the same row.
here an example of the idea, setting a max-height from 0 to 200px and a transition.
it toggles a class.
$(document).ready(function() {
$(".collapsible").click(function() {
this.classList.toggle('slide');// plain javascript to toggle a class
});
});
*{box-sizing:border-box}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p{margin-top:0;}
.collapsible:hover {
background: #aaf;
}
.content {
margin:0 0.5em;
margin-left: 16px;
max-height:0; /* Initially collapsed */
transition:0.5s
}
.slide + .content{max-height:400px;/* which one got the class*/ color:darkred}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible"><h2>Box 1</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box2">
<div class="collapsible"><h2>Box 2</h2></div>
<div class="content"><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box3">
<div class="collapsible"><h2>Box 3</h2></div>
<div class="content"><p>Content</p></div>
</div>
<div class="box" id="box4">
<div class="collapsible"><h2>Box 4</h2></div>
<div class="content"><p>Content</p><p>Content</p><p>Content</p></div>
</div>
<div class="box" id="box5">
<div class="collapsible"><h2>Box 5</h2></div>
<div class="content"><p>Content</p></div>
</div>
</section>
</body>
What you are missing here , is to set the height of the row to the height of the taller box, unless they will all be the same height.
For this, you can look for every box at the same top offset that the one clicked, and look for the tallest innercontent of the .contents and use this height .
Here comes the idea looking where each are standing and resets a max-height rule, you can inspire from too:
// defaut : supposed to be with no class 'slide' set on html elements
$(document).ready(function () {
let boxes = document.querySelectorAll(".collapsible");
let contents = document.querySelectorAll(".content");
$(".collapsible").click(function (event) {
let arrayContent = [];//reset
let hide=false;
for (i = 0; i < contents.length; i++) {
contents[i].style.maxHeight = "min-content";
let index = i;
let heightC = contents[i].offsetTop;
let boxOffset = contents[i].parentNode.offsetTop + 1;
// a few infos .add/remove what is needed
arrayContent.push({
index,
heightC,
boxOffset
});
contents[i].setAttribute("style", "");// resets inline style
}
let rowOffset = this.offsetTop;
if(this.classList.contains('slide')) {
hide=true;
}
let classState = this.classList;
arrayContent.forEach(function (obj) {
if (obj.boxOffset == rowOffset) {
if(hide == true) boxes[obj.index].classList.add('slide');/* reset needed if window resized => rows reorganized ? */
boxes[obj.index].classList.toggle("slide");
} else {
boxes[obj.index].classList.remove("slide");
}
});
});
});
* {
box-sizing: border-box
}
#containers {
display: flex;
flex-wrap: wrap;
gap: 1em;
}
.box {
min-width: 15em;
background: #888;
border: #555 1px solid;
overflow: hidden;
border-radius: 0.5em;
}
.collapsible {
background: #ccc;
cursor: pointer;
}
.collapsible h2 {
margin: 0;
margin-left: 16px;
font-size: 2rem;
}
p {
margin-top: 0;
}
.collapsible:hover {
background: #aaf;
}
.content {
margin: 0 0.5em;
margin-left: 16px;
max-height: 0;
/* Initially collapsed */
transition: max-height 0.5s!important
}
.slide~.content {
max-height: 400px;
/* which one got the class*/
color: darkred;
}
.collapsible:before {
content: attr(class)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body>
<p id="status"></p>
<section id="containers">
<div class="box" id="box1">
<div class="collapsible">
<h2>Box 1</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box2">
<div class="collapsible">
<h2>Box 2</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box3">
<div class="collapsible">
<h2>Box 3</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
<div class="box" id="box4">
<div class="collapsible">
<h2>Box 4</h2>
</div>
<div class="content">
<p>Content</p>
<p>Content</p>
<p>Content</p>
</div>
</div>
<div class="box" id="box5">
<div class="collapsible">
<h2>Box 5</h2>
</div>
<div class="content">
<p>Content</p>
</div>
</div>
</section>
</body>

Center last element if it's last and alone in a row

I got a bootstrap grid where the content is loaded dynamically.
If the last element is alone in the row it should be centered.
What i want:
A B
C D
E F
G
HTML Code:
div {
border: 1px solid red;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<div class="row">
<div class="col-md-6">a</div>
<div class="col-md-6">b</div>
<div class="col-md-6">c</div>
<div class="col-md-6">d</div>
<div class="col-md-6">e</div>
<div class="col-md-6">f</div>
<div class="col-md-6">g</div>
</div>
What i get:
A B
C D
E F
G
I tried it with the css selector :nth-last-of-type(), but with this selector every 'last element' gets the properties.
Anyone know how to do this? I hope i could explain it in a way you can understand it.
It's simple, you can use display:flex and text-align: center.
.flex-container {
padding: 0;
margin: 0;
list-style: none;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-flex-flow: row wrap;
justify-content: space-around;
}
.flex-item {
background: tomato;
padding: 5px;
width: 200px;
height: 150px;
margin-top: 10px;
line-height: 150px;
color: white;
font-weight: bold;
font-size: 3em;
text-align: center;
}
<ul class="flex-container">
<li class="flex-item">1</li>
<li class="flex-item">2</li>
<li class="flex-item">3</li>
<li class="flex-item">4</li>
<li class="flex-item">5</li>
<li class="flex-item">6</li>
<li class="flex-item">7</li>
</ul>
Combining last-child and nth child would work, then you can position your element centrally. This example uses left 50% transform:translateX(-50%);
https://codepen.io/anon/pen/zPNWxK
<div class="row">
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
</div>
div {
height:20px;
box-sizing:border-box;
background-color:blue;
border:1px solid #fff;
}
div:last-child:nth-child(odd) {
background-color:red;
left:50%;
transform:translateX(-50%);
}
When you actually do this don't use the div as the actual selector of course
You can combine :nth-child(odd) and :last-child to check if the element is the last in the row by itself like this:
CSS:
.row {
width: 200px;
}
.col-md-6 {
width: 50%;
float: left;
text-align: center;
}
.col-md-6:nth-child(odd):last-child {
width: 100%;
}
HTML:
<div class="row">
<div class="col-md-6">a</div>
<div class="col-md-6">b</div>
<div class="col-md-6">c</div>
<div class="col-md-6">d</div>
<div class="col-md-6">e</div>
<div class="col-md-6">f</div>
<div class="col-md-6">g</div>
</div>
Here's a Codepen.
if you are sure that the last one is odd (so it will be alone in that row) you can use bootstrap offsetting system and write it like:
<div class="row">
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6"></div>
<div class="col-md-6 col-md-offset-3"></div>
ref: Bootstrap OffSet
Just text-align: center the last-child div, as below.
Open the snippet in full page mode to see the result as you wanted
div:last-child{
text-align: center;
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
<div class="row">
<div class="col-md-6">A</div>
<div class="col-md-6">B</div>
<div class="col-md-6">C</div>
<div class="col-md-6">D</div>
<div class="col-md-6">E</div>
<div class="col-md-6">F</div>
<div class="col-md-6 ">G</div>
</div>
It is quite simple in that way:
<div class="col-md-6 mx-auto"></div>
I think you are looking for :last-child selector

jQuery to clean flexbox structure from empty columns and rows

I am creating a jQuery to optimize table structure by removing all the extra rows and columns that:
a) contain less than 2 child elements
b) contain duplicate container (.column can't have child .column, .row can't have child .row)
I still didn't get it working, it runs in 6 steps (visible in the comments).
What is necessary to turn the #example into #solution?
var selection;
//1. Select all rows and columns
selection = $('#example').find('.row, .column');
//2. Deselect rows and columns with more than 1 child
selection = selection.not('.row:has(div:nth-of-type(2)), .column:has(div:nth-of-type(2))');
//3. Deselect rows and columns that contain cells
selection = selection.not('.row:has(.cell:nth-of-type(1)), .column:has(.cell:nth-of-type(1))');
//4. Remove redundant divs
console.log(selection);
//selection.css('border', '1px yellow solid');//paint them yellow (for testing)
selection.children().first().unwrap();
//5. Select duplicate rows and columns
selection = $('#example').find('.row:has(.row), .column:has(.column)');
//6. Remove redundant divs since first removal
selection.children().first().unwrap();
.grid {
display: flex;
min-height: 5px;
}
.row {
display: flex;
flex-grow: 1;
border: 1px solid red;
}
.cell {
flex-grow: 1;
flex-basis: 20px;
border: 1px solid green;
}
.column {
display: flex;
flex-grow: 1;
flex-direction: column;
border: 1px solid blue;
}
.row,
.column,
.cell {
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span>Example</span>
<div id="example" class="grid">
<div class="column">
<div class="row">
<div class="column">
<div class="row">
<div class="column">
<div class="cell">
</div>
</div>
</div>
</div>
<div class="column">
<div class="row">
<div class="cell">
</div>
<div class="cell">
</div>
</div>
</div>
</div>
</div>
</div>
<span>Solution</span>
<div id="solution" class="grid">
<div class="row">
<div class="cell">
</div>
<div class="cell">
</div>
<div class="cell">
</div>
</div>
</div>
Based on the formula you have given, try this demo
$('#example').find('.column').each(function(){
$(this).find(".column").each(function(){
$(this).unwrap();
});
if ($(this).children().size() < 2 && $(this).find(".cell").size() == 0 )
{
$(this).remove();
}
});
$('#example').find('.row').each(function(){
$(this).find(".row").each(function(){
$(this).unwrap();
});
if ($(this).children().size() < 2 && $(this).find(".cell").size() == 0 )
{
$(this).remove();
}
});
This is a solution to updated version of the constraints, constraint A was reduced to minimum to provide an even better result (it is possible to reduce this example to bare minimum of one row).
//Constraint A
$('#example').find('.row, .column').each(function() {
if ($(this).children().size() < 2) {
$(this).children().first().unwrap();
}
});
//Constraint B
$('#example').find('.column').each(function() {
if ($(this).parent().hasClass('column')) {
$(this).children().first().unwrap();
}
});
$('#example').find('.row').each(function() {
if ($(this).parent().hasClass('row')) {
$(this).children().first().unwrap();
}
});
.row {
display: flex;
flex-grow: 1;
border: 1px solid red;
}
.cell {
flex-grow: 1;
flex-basis: 20px;
border: 1px solid green;
}
.column {
display: flex;
flex-grow: 1;
flex-direction: column;
border: 1px solid blue;
}
.row,
.column,
.cell {
margin: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<span>Solution</span>
<div id="example" class="row">
<div class="column">
<div class="row">
<div class="column">
<div class="row">
<div class="column">
<div class="cell">
</div>
</div>
</div>
</div>
<div class="column">
<div class="row">
<div class="cell">
</div>
<div class="cell">
</div>
</div>
</div>
</div>
</div>
</div>

Categories