Arrange elements in columns vertically shifted relative to one another

ぐ巨炮叔叔 提交于 2019-12-11 06:08:20

问题


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):

  1. All elements: top margin of 0, left/right margins of 1%, bottom margin of 10px.
  2. Second element (first element in right-hand column): top margin of 50px.
  3. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!