How to arrange intersected sections with variable heights [duplicate] - css

This question already has answers here:
CSS-only masonry layout
(4 answers)
Closed 11 months ago.
I have a report that has to be in blocks (divs), these divs must be aligned horizontally (with width of 50% for each), meaning that the second is to right of the first, and the third must be below the first one regardless of the height of the second.
My description might be a little fuzzy, so I attached an image that represents the idea:
Sample:
Thank you very much in advance.
I tried normal CSS hacks (float, position, display) and so on; and it didn't work.
I tried grid layout, and I tried to use Bootstrap properties; in all the above, block number 3 starts, yes, below block one but after the end of block number 2 height.

Try this:
.maindiv { /* Masonry container */
column-count: 2;
column-gap: 1em;
}
.item { /* Masonry bricks or child elements */
display: inline-block;
margin: 0 0 1em;
width: 100%;
}

here's a quick way of doing it with flexbox, link to codepen. The downside is that you would need to have 2 columns, so on mobile, the second col would go below the first one. Ideally, you would do this with CSS Grid, or JS Masonry plugin
And here's the code itself:
HTML:
<div class="example-wrap">
<div class="col">
<div class="card" style="height: 100px;"></div>
<div class="card"></div>
<div class="card" style="height: 400px;"></div>
</div>
<div class="col">
<div class="card"></div>
<div class="card" style="height: 400px;"></div>
<div class="card" style="height: 150px;"></div>
</div>
CSS:
.example-wrap {
display: flex;
width: 600px;
justify-content: space-between;
align-items: flex-start;
box-shadow: 0 0 0 1px black;
}
.col {
width: calc((100% - 30px) / 2);
}
.card {
width: 100%;
height: 300px;
box-shadow: 0 0 0 1px red;
margin-bottom: 30px;
}

Masonry is the best way to do what you want.
var elem = document.querySelector('.grid');
var msnry = new Masonry( elem, {
// options
itemSelector: '.grid-item',
columnWidth: 200
});
// element argument can be a selector string
// for an individual element
var msnry = new Masonry( '.grid', {
// options
});
.grid-item {
width: 40%;
margin: 20px;
border: 1px solid black;
height: 100px;
text-align: center;
color: red;
font-weight:700;
}
.height-2 {
height:300px;
}
<script src="https://unpkg.com/masonry-layout#4.2.2/dist/masonry.pkgd.min.js"></script>
<div class="grid">
<div class="grid-item">1</div>
<div class="grid-item height-2">2</div>
<div class="grid-item height-2">3</div>
<div class="grid-item">4</div>
</div>
See Masonry for more options/methods : https://masonry.desandro.com/

Related

CSS - spaces between items on multiple lines [duplicate]

This question already has answers here:
Targeting flex items on the last or specific row
(10 answers)
Closed 1 year ago.
I have a container with a dynamic number of items.
Each line can contain up to 4 items. If there are more than 4 items, the next item will start a new line (image 1). If there are less than 4 items it's OK, they just won't fill the whole line (image 2).
But I'm having troubles with the spaces between them:
I tried to use margin-right but it affects the last items in the lines (e.g.: item #4).
I tried to use justify-content: space-between but it looks good only for 4 items and up. For 3 and bellow, it creates a big space between them and I want them to look as in image 2.
Any other elegant / easy solutions?
.container {
display: flex;
flex-wrap: wrap;
/* justify-content: space-between; */
}
.item {
display: inline-block;
width: calc(25% - 12px);
/* margin-right: 12px; */
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
You can use css grid, you have to use display: grid;, use grid-template-columns to set the amount of columns that you want (1fr = 1 parent container fraction) and finally use grid-gap to set the space between your items.
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
</div>
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr;
grid-gap: 12px;
}
.item {
display: inline-block;
border: 1px solid red;
color: red;
}
<div class="container">
<div class="item">
#1
</div>
<div class="item">
#2
</div>
<div class="item">
#3
</div>
<div class="item">
#4
</div>
<div class="item">
#5
</div>
<div class="item">
#6
</div>
</div>
More info about Css grid Here!
In class .item, is defined width with calc(25% - 12px). Remember, 25% is just 4 items in each line. 20% is 5 items in each line.
So, change the width to calc(20% - 12px)
While CSS Grid is possibly the better solution for the problem, it's entirely possible to solve the problem with CSS flex-box layout, using the gap property and taking advantage – as did your original code – of the calc() function:
// this is to allow you to dynamically add more .item elements
// so you see that it should meet your needs containing more
// elements.
// we use document.querySelector() to retrieve the first element
// that matches the selector (if any exist, otherwise null):
const button = document.querySelector('button'),
// defining a named function to handle addition of new .item
// elements:
addMore = () => {
// finding the first .item element on the page:
let base = document.querySelector('.item');
// finding the .container element, and using
// .append() to attach a cloned copy of the first
// .item:
document.querySelector('.container').append(base.cloneNode(true));
}
// binding the named - addMore() - function as the event-handler
// for the 'click' event:
button.addEventListener('click', addMore);
*,
::before,
::after {
/* selecting all elements, and the pseudo-elements ::before
and ::after, setting their box-sizing model to border-box
in order that their widths include their border and padding
*/
box-sizing: border-box;
/* removing margin and padding: */
margin: 0;
padding: 0;
}
.container {
display: flex;
flex-wrap: wrap;
/* using the gap property to place a 0.5em 'gutter'
between adjacent elements, both horizontally and
vertically: */
gap: 0.5em;
}
.item {
/* setting the flex-grow: to 1, flex-shrink to 0,
and flex-basis to the result of 20% of the parent-
width minus 0.5em (the gap-space): */
flex: 1 0 calc(20% - 0.5em);
}
/* irrelevant, purely for aesthetics */
*,
::before,
::after {
line-height: 2em;
}
.container {
border: 1px solid #000;
counter-reset: itemCount;
width: 90vw;
margin-inline: auto;
}
.item {
background-color: lightblue;
flex: 1 0 calc(20% - 0.5em);
}
.item:nth-child(odd) {
background-color: palegreen;
}
.item::before {
content: counter(itemCount, decimal-leading-zero);
counter-increment: itemCount;
}
<button type="button">Add another item</button>
<div class="container">
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
<div class="item">
</div>
</div>
JS Fiddle demo.
References:
box-sizing.
calc().
gap.
margin-inline.

2 columns layout in css (ordering items)

Is there any way to show 2 columns layout with this html code using only css? (column1 class should be in first column, and column2 class in the second one)
<div class="container">
<div class="column1">1a</div>
<div class="column2">1b</div>
<div class="column2">2b<br>zz</div>
<div class="column1">2a</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column2">5b</div>
<div class="column1">3a</div>
<div class="column1">4a</div>
</div>
The result I expect:
1a 1b
2a 2b
3a zz
4a 3b
4b
5b
float seems the best option here if you manage the Block formatting context
example :
* {
box-sizing: border-box;
}
div div {
box-shadow: inset 0 0 0 1px;/* see me */
width: 50%;
line-height: 1.4em; /* because of that 2 lines element , to hide the gap */
background: lightblue;/* see me too */
}
.column1:first-child {
float: left; /* let another float stand aside */
}
.column2 {
float: right;
clear: right;/* pile us to the far right */
}
div div:nth-child(odd) {
background: lightgreen;/* see me different */
}
<div class="container">
<div class="column1">1a</div>
<div class="column2">1b</div>
<div class="column2">2b<br>zz</div>
<div class="column1">2a</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column2">5b</div>
<div class="column1">3a</div>
<div class="column1">4a</div>
</div>
It matches your expected layout here, but will it also with real content ?
One way you can do this would be to float the classes of .column1 and .column2 like this:
.container {
width: 100%;
}
.column1 {
float: left;
width: 50%;
}
.column2 {
float: right;
width: 50%;
}
<div class="container">
<div class="column1">1a</div>
<div class="column2">1a</div>
<div class="column1">2b</div>
<div class="column1">3a</div>
<div class="column2">2b</div>
<div class="column2">3b</div>
<div class="column2">4b</div>
<div class="column1">4a</div>
<div class="column1">5a</div>
</div>
You could also maybe simplify this by having only two inner divs representing each column like:
<div class="container">
<div class="column1">
...
</div>
<div class="column2">
...
</div>
</div>
You can also achieve the same thing using minimal code via using flex. Just add following code to your CSS.
.container {
display: flex;
flex-wrap: wrap;
}
.container div {
width: 50%;
}
Explanation :- You add display type flex to the container, so all the elements will be arranged next to each other inside this div.
Now when you give width of 50% to internal divs and give flex-wrap: wrap to the container, it ensures that only 2 divs are next to each other since there is no space left to accommodate more.
Alternatively if you want the items closer to each other you can reduce the width of the outer container.
.container {
display: flex;
flex-wrap: wrap;
width: 10%;
}
.container div {
width: 50%;
}
This would be closer to what you are expecting.

Flex box requires width for proper sizing [duplicate]

This question already has answers here:
Why don't flex items shrink past content size?
(5 answers)
What are the differences between flex-basis and width?
(6 answers)
Closed 3 years ago.
Using flex for the main menu that has three boxes. The first and third do not flex, and the second grows to fill. The second box is a nested flex that has two boxes, the first does not flex and the second grows to fill. The nested flex, second box is configured to use ellipsis for overflow, but that did not work. The box expands and pushes the nested flex, but not the parent flex, beyond the parent max width. Then discovered if the second boxe has a defined width, any value, even 1px, it works as expected. Concerned and courious why that is, and if i'm doing something wrong.
Codepin to see in action: https://codepen.io/nws-jholmberg/pen/mdyEyWq
<div class="menu-container">
<div class="menu">
<div class="menu-item-1 menu-item">X</div>
<div class="menu-item-2 menu-item">
<div class="menu-search">
<div class="menu-item-search-1">X</div>
<div class="menu-item-search-2">The search result goes here and does not fit</div>
</div>
</div>
<div class="menu-item-3 menu-item">X</div>
</div>
</div>
<br>
<div class="menu-container">
<div class="menu">
<div class="menu-item-1 menu-item">X</div>
<div class="menu-item-2 menu-item">
<div class="menu-search">
<div class="menu-item-search-1">X</div>
<div class="menu-item-search-2 add-width">The search result goes here and does not fit</div>
</div>
</div>
<div class="menu-item-3 menu-item">X</div>
</div>
</div>
.menu-container {
background-color: #f00;
max-width: 200px;
}
.menu {
display: flex;
}
.menu-item {
margin: 4px;
}
.menu-item-1 {
flex: none;
}
.menu-item-2 {
flex: 1;
background-color: #0ff;
}
.menu-item-3 {
flex: none;
}
.menu-search {
display: flex;
}
.menu-item-search-1 {
flex: none;
background-color: #3A3;
color: #fff;
}
.menu-item-search-2 {
flex: 1;
background-color: #3F3;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.add-width {
width: 1px;
}
there is 2 other hurtless ways you can use :
.menu-item-2 {
flex: 1;
background-color: #0ff;
overflow:hidden;
}
or
.menu-item-2 {
flex: 1;
background-color: #0ff;
min-width:0;
}
https://codepen.io/gc-nomade/pen/yLyJypm

Target :last-child with mixed class siblings?

How do I target the last child of a class when there are siblings with another class after it? It would be nice to have a :last-sibling and :last-sibling-of-type.
FIDDLE
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div> /* How do I target this if its nth position is unknowable? */
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
</div>
If you need to get the last .grid-item element out of the document, regardless of what it's inside, then you can't do that in CSS. In CSS, you can only select the first, last, or nth element at one particular level of the hierarchy, you can't select the last element of some type regardless of what it's nested in.
Here is one way to get the last div inside of .grid
.grid div:last-of-type
Here's another way to get the last child of some outer div:
div :last-child
BUT The thing you probably need is some js:
You could do this the jquery approach (like below), or just do getElementsByClassNameand then set the last element in the list in the same manner.
function getlastchild() {
var items = $(".grid-item");
items.last().css("background-color", "red");
//OR even as one-liner: $(".grid-item").last().css("background-color", "red");
}
.grid {
width: 200px;
}
.grid div:last-of-type {
color: white;
}
div:last-child {
background-color: grey;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="grid">
<div class="grid-item">1</div>
<div class="grid-item">2</div>
<div class="grid-item">3</div>
<div class="grid-item">4</div>
<div class="grid-orphan">5</div>
<div class="grid-orphan">6</div>
<div class="grid-orphan">7</div>
</div>
<button onclick="getlastchild()">Press me to get last grid-item</button>
I see 4 possibilities here:
Case 1:
You already know the number of grid-orphan items, so you can use nth-last-child.
.grid-item:nth-last-child(4) {
background: blue;
}
Case 2:
You are an adventurer and this is not for production: use the newest version of nth-child and nth-last-child:
.grid-item:nth-last-child(1 of .grid-item) {
background: blue;
}
However, it only works on Safari for now (12.99%).
See specs
Case 3:
Use JavaScript.
const items = document.querySelectorAll('.grid-item')
const selectedItem = [].slice.call(items).pop();
selectedItem.style.background = 'blue';
Case 4:
Just add an extra class.
<div class="grid-item grid-item--special"></div>
.grid-item.grid-item--special {
background: blue;
}
You can use either :nth-last-child or :nth-last-of-type if you are able to put all your .grid-orphan elements inside another div.
HTML
<div class="grid">
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-item"></div>
<div class="grid-orphans">
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
<div class="grid-orphan"></div>
</div>
</div>
SCSS
.grid {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
.grid-item {
background: gray;
border: 1px solid white;
position: relative;
display: flex;
flex-direction: column;
height: 100px;
width: calc(25% - 20px);
min-width: 360px;
flex-grow: 2;
justify-content: flex-start;
cursor: pointer;
&:nth-last-child(2) {
background: navy;
}
}
.grid-orphan {
height: 0;
border: 0;
display: flex;
flex-direction: column;
width: calc(25% - 20px);
min-width: 360px;
flex-grow: 2;
justify-content: flex-start;
cursor: pointer;
}
}
Jsfiddle available here.
Otherwise, a CSS-only approach would be limited to only Apple's browsers, that is Safari desktop and on iOS, since they are the only ones that implement the selector list argument, which allows you to narrow down the elements by a class selector. For seeing this just change &:nth-last-child(2) with &:nth-last-child(1 of .grid-item)
All those answers are great. Thank you. I ended up just making the orphans <span>'s and then .grid-item:last-child worked fine. I knew I could do that but was wondering if it could be done (easily) when the orphans were the same element type as the grid items.

Is there a way of writing CSS that lets you re-organize the order of how HTML components appear?

I am working on a responsive site in which the mobile/tablet view differs from the desktop view in the way it re-orders the DIVs.
Is there a way to write maintainable CSS that let's you re-organize the order of how HTML DIVs appear?
For example, the code below controls the order of how DIVs would appear on a desktop device:
<div class="container">
<div class="row1">
<div class="col1A">Sample content</div>
<div class="col2A">Sample content</div>
<div class="col3A">Sample content</div>
</div>
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
</div>
However, for mobile/tablet view, I want to display the DIVs in different order using CSS, like the example below:
Show row2, col2B
Then row1, col1A
Then row1, col3A
Then row2, col1B
Is this possible using CSS ?
As a proof-of-concept, you can use the flex CSS property to reorder how elements are visually rendered.
In your example, I had to keep the child elements within a single container
and then I could control the order using the order property.
If you want to hide some items on the small screen view, use display: none on the specific items.
Note: For a wide screen, you would need some CSS rules to get the items to look like two rows. (Please specify what you need.)
If you combine this with media queries, you can get a workable solution.
.container {
display: flex;
flex: center;
flex-direction: column;
align-items: center;
border: 1px dotted blue;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
width: auto;
}
.col1A {
order: 2;
}
.col2A {
display: none;
}
.col3A {
order: 3;
}
.col1B {
order: 4;
}
.col2B {
order: 1;
}
.col3B {
display: none;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
If you want to simulate two rows of three elements, you can still use flex with some adjustments. The following may be helpful.
.container {
display: flex;
flex: center;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
border: 1px dotted blue;
padding: 20px 0;
}
.container .row1 {
margin-bottom: 20px;
}
.container div {
text-align: center;
padding: 10px;
border: 1px dotted gray;
flex-basis: calc(33% - 20px);
}
.col1B {
background-color: yellow;
}
<div class="container">
<div class="row1 col1A">Sample content 1A</div>
<div class="row1 col2A">Sample content 2A</div>
<div class="row1 col3A">Sample content 3A</div>
<div class="row2 col1B">Sample content 1B</div>
<div class="row2 col2B">Sample content 2B</div>
<div class="row2 col3B">Sample content 3B</div>
</div>
Set a screen size for mobile device detection in the css and add the following
#media screen and (max-width: SIZE) {
.row2{
display: flex; flex-flow: column;
}
.col1B{
order: 1;
}
.col2B{
order: 2;
}
.col3B{
order: 3;
}
}
And then add the classes to the DIVs
<div class="row2">
<div class="col1B">Sample content</div>
<div class="col2B">Sample content</div>
<div class="col3B">Sample content</div>
</div>
Change order: 1/2/3; to your needs.

Resources