问题
I am looking for a way to achieve this effect:
On the mobile layout, elements are placed one under the other in a column:
On the desktop layout, however, elements are placed in two columns, with odd elements in one column and even elements in the other. The columns are shifted vertically relative to one another:
I wonder whether there is a CSS-only way to achieve this positioning. CSS grid comes tantalizingly close, if only there were a way to say that, for example, nth-child(n+1)
would span rows 1 and 2, and `nth-child(2n) would span rows 2 and 3, and so on, but I am not sure whether CSS grid can do this. Do you perhaps know of a way to do this?
UPDATED: I guess this is a very rough CSS approximation of what I have in mind, using inline blocks: https://codepen.io/anon/pen/JwXVox. I would welcome a better technique.
回答1:
You can do it with the Flexbox, calc()
function, :nth-child()
selectors, :not()
pseudo-class and of course @media
queries:
.flex {
display: flex; /* displays flex-items (children) inline */
flex-wrap: wrap; /* enables their wrapping */
}
.flex > div {
flex-basis: calc(50% - 10px); /* initial width set to 50% of the parent's - left & right margins */
height: 100px;
margin: 0 5px; /* preferably top & bottom margins set to zero to keep calculations simple */
border: 1px solid;
box-sizing: border-box; /* because of the borders, also recommended in general */
}
.flex > div:nth-child(2){
margin-top: 55px; /* half of divs height + half of the desired gap between them; modification affects all but the first div */
}
.flex > div:nth-child(odd):not(:first-child){
margin-top: -45px; /* - half of divs height + half of the desired gap between them, so in this case the gap is 10px (2 x 5px); modification affects all but the first odd div (if ofc. modification is "") */
}
.flex > div:nth-child(even):not(:nth-child(2)){
margin-top: 10px; /* gap size; modification affects all but the first three divs */
}
@media (max-width: 600px){
.flex {flex-direction: column} /* stacks flex-items vertically */
.flex > div {flex-basis: auto} /* initial width set to default or 100% */
.flex > div:not(:first-child) {margin-top: 10px !important}
}
<div class="flex">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</div>
回答2:
Presumably, you know how to do your mobile version, and how to use @media
to distinguish between different screen sizes.
I didn't fully understand what you meant by "span rows 1 and 2," but you don't have to use a grid layout. Good old-fashioned inline-blocks work fine here. Your rough approximation is rough only because your margin values are roughly estimated, not because the technique you're using is insufficient to do what you want.
Here's what I did to position the .grid-element
elements (and it looks pretty even to me; perhaps it's close enough to what you want for you to fine tune it):
- All elements: top margin of 0, left/right margins of 1%, bottom margin of 10px.
- Second element (first element in right-hand column): top margin of 50px.
- All odd-numbered elements except the first one: top margin of -50px.
You can experiment with the different margin values to see the effect that they have. But basically, once you have them set up side by side, you bump the second one down. That positions the second and first elements correctly, but all the subsequent elements get bumped down as well. So, we want to bump the even-numbered ones back up with a negative margin value. However, we don't want to bump the first one up, since it never got bumped down. So, we exclude it with the nth-child(2n+3)
(i.e. every second element starting with three) selector. This is the equivalent of using nth-child(odd):not(:first-child)
.
function fillGrid() {
const container = document.querySelector('.container');
const times = [...new Array(10)];
times.forEach(() => {
const element = document.createElement('div');
element.classList.add('grid-element');
container.appendChild(element);
});
}
fillGrid()
.container {
width: 60%;
margin: auto;
background: papayawhip;
padding-bottom: 60px;
}
.grid-element {
display: inline-block;
height: 50px;
background: green;
vertical-align: middle;
width: 48%;
margin: 0 1% 10px;
}
.grid-element:nth-child(2) {
margin-top: 60px;
}
.grid-element:nth-child(2n+3) {
margin-top: -60px;
}
<div class="container">
</div>
EDIT: Using the CSS grid to get the same result
After our conversation in the comments, I experimented with position:grid
to see if I could figure out a way to do it. I had to add a line of javascript (or write a rather cumbersome bunch of CSS) to get it done, but it seems to work out.
I just set the row-height to 25%, and then added a line in your javascript to make each row two lines thick, and start it one line down from the previous element.
function fillGrid() {
const container = document.querySelector('.container');
const times = [...new Array(10)];
times.forEach((e, index) => {
const element = document.createElement('div');
element.classList.add('grid-element');
//added the line below to size and position the elements
element.setAttribute("style", "grid-row-start: " + (index + 1) + "; grid-row-end: " + (index + 3) + ";");
element.textContent = index + 1; //also put a number in each box
container.appendChild(element);
});
}
fillGrid()
.container {
display: grid;
grid-gap: 5px;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 25px;
width: 60%;
margin: auto;
background: papayawhip;
}
.grid-element {
background: green;
color: white;
}
<div class="container">
</div>
回答3:
A float
approach
.container {
width: 250px;
margin: 0 auto;
}
.item {
width: 100px;
height: 100px;
border: 1px solid black;
margin-top: calc(100px/3);
}
.item:first-child {
margin-top: 0;
}
@media (min-width: 400px) {
.item:nth-child(even) {
float: right;
margin-top: calc(-100px/3);
}
}
<div class="container">
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
<div class="item"></div>
</div>
来源:https://stackoverflow.com/questions/53799209/arrange-elements-in-columns-vertically-shifted-relative-to-one-another