【问题标题】:Split items into two columns CSS gridSplit items into two columns CSS grid
【发布时间】:2022-12-28 03:49:50
【问题描述】:

Is there a way to achieve the following via CSS grid?

  1. If the number of items is odd, the first column should have 1 more row than the second column.
    1 4
    2 5
    3
    
    1. If the number of items is even, the columns should have the same number of rows.
    1 3 
    2 4
    

    Tried using grid-template-rows but the number of rows per column is fixed.

【问题讨论】:

  • No. the number of rows is required to be stated for this to work and even then CSS can't actuallycounthow many items there are.
  • it will always be 5 or 4 elements? if yes, it's possible
  • @TemaniAfif no, it's either odd or even number of elements.
  • I updated my answer to cover the generic case

标签: css css-grid


【解决方案1】:

UPDATED: Added two more options how to handle it by HTML/CSS. What about using grid? There is 3 options, how it can be done:

  1. Using default item order;
  2. Using CSS variable, where you can define row number to break;
  3. Using additional class, added to child item, after wich it's siblinks will go to next column;

    /* Option-1. Default */
    .container {
      display: grid;
      grid-template-columns: repeat(2, minmax(50px, 1fr));
      grid-gap: 1rem;
      width: fit-content;
    }
    
    .container > div {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      background-color: var(--color, tomato);
      aspect-ratio: 1/1;
    }
    /* End of Option-1. */
    
    /* Option-2. With additional class */
    .container--with-break {
      grid-auto-flow: column;
    }
    .container--with-break > div {
      grid-column: 1;
    }
    .container--with-break > .break ~ div {
      grid-column: 2;
    }
    /* End of Option-2. */
    
    /* Option-3. With CSS variable */
    .container--with-break-row {
      grid-auto-flow: column;
      grid-template-rows: repeat(var(--break-row), 1fr);
    }
    /* End of Option-3. */
    
    /* Example stylings */
    .wrapper {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      grid-gap: 2rem;
      align-items: start;
      width: fit-content;
    }
    
    hr {
      margin-top: 2rem;
      margin-bottom: 2rem;
    }
    /* End of Ex. stylings */
    <p>Option with default order</p>
    <div class="wrapper">
      <div class="container">
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
      </div>
    
      <div class="container">
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
      </div>
    </div>
    
    <hr />
    
    <p>Option with changing order. Using CSS variables</p>
    <div 
      class="wrapper"
      style="--color: mediumturquoise;"
    >
      <div 
        class="container container--with-break-row"
        style="--break-row: 3;"
      >
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
      </div>
      
      <div 
        class="container container--with-break-row"
        style="--break-row: 2;"
      >
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
      </div>
      
      <div 
        class="container container--with-break-row"
        style="--break-row: 3;"
      >
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        <div>6</div>
      </div>
      
      <div 
        class="container container--with-break-row"
        style="--break-row: 4;"
      >
        <div>1</div>
        <div>2</div>
        <div>3</div>
        <div>4</div>
        <div>5</div>
        <div>6</div>
        <div>7</div>
      </div>
    </div>
    
    <hr />
    
    <p>Option with changing order. Using additional class to child item</p>
    <div 
      class="wrapper"
      style="--color: mediumpurple;"
    >
      <div class="container container--with-break">
        <div>1</div>
        <div>2</div>
        <div class="break">3</div>
        <div>4</div>
        <div>5</div>
      </div>
      
      <div class="container container--with-break">
        <div>1</div>
        <div class="break">2</div>
        <div>3</div>
        <div>4</div>
      </div>
    </div>

【讨论】:

  • @Randy could be an option, if you have 4-5 element max. but anyway I prefer not to hardcode things
  • You could re-arrange the HTML elements to get the layout asked for in the question. I would think if you split the elements into two sections, first half and second half, then you can alternate between the first and second half to get this list dynamically.
  • dense will do nothing here since the elements are placed using the default algorithm. dense is only useful when we give explicit placement to some elements
  • @TemaniAfif you are right, I was trying another way to handle it, and forgot to remove (and later described it above) Removed unused code.
  • Already tried this but really need it to be top to bottom, left to right.
【解决方案2】:

If the number of items is always 4 or 5 you can do the following:

.container {
  display: grid;
  grid-gap: 1rem;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  justify-content: start;
  margin: 10px;
  border: 2px solid red;
}

.container>div {
  background-color: lightblue;
  padding: 20px;
}

/* create the extro row for the case of odd items */
.container>div:nth-child(3):nth-last-child(odd) {
  grid-row: 3;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

For the generic case like below

.container {
  display: grid;
  grid-gap: 1rem;
  grid-template-rows: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  grid-auto-flow: dense; /* you need this */
  justify-content: start;
  margin: 10px;
  border: 2px solid red;
}

.container>div {
  background-color: lightblue;
  padding: 10px;
}

/* create the extro row for the case of odd items */
.container>div:nth-child(3):nth-last-child(odd) {
  grid-row: 3;
}
/* all odd items counting from the end at the second row*/
.container > div:nth-last-child(odd) {
 grid-row: 2;
}

/* the first and second item should never change*/
.container > div:first-child {
 grid-row: 1;
}
.container > div:nth-child(2) {
 grid-row: 2;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
</div>

<div class="container">
  <div>1</div>
  <div>2</div>
  <div>3</div>
  <div>4</div>
  <div>5</div>
  <div>6</div>
  <div>7</div>
</div>

<div class="container">
  <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>9</div>
</div>

<div class="container">
  <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>9</div>
  <div>10</div>
</div>

【讨论】:

    【解决方案3】:

    Simplifying Yaro's answer:

    <div style="display: grid; grid-auto-flow: column; grid-template-rows: repeat(3, 1fr)">
      <div>1</div>
      <div>2</div>
      <div>3</div>
      <div>4</div>
      <div>5</div>
    </div>
    

    Is all you need. This results in the items going:

    1    4
    2    5
    3
    

    instead of

    1    2
    3    4
    5
    

    Since we split it into 3 rows with repeat(3, 1fr).

    【讨论】:

      【解决方案4】:

      Credits for the grid go to @YaroslavTrach but the order of HTML elements can be achieved by splitting your elements into two arrays and then alternating between them when rendering.

      const elements = [ 1, 2, 3, 4, 5 ];
      const half = Math.ceil(elements.length / 2);
      
      const firstHalf = elements.slice(0, half);
      const secondHalf = elements.slice(-half + 1);
      
      const toDiv = (i) => i ? `<div>${i}</div>` : ``;
      
      document.getElementById('container').innerHTML = `
        ${firstHalf.map((el, id) => toDiv(el) + toDiv(secondHalf[id])).join('')}
      `;
      #container {
        display: grid;
        grid-template-columns: repeat(2, minmax(50px, 1fr));
        grid-gap: 1rem;
        width: fit-content;
      }
      
      #container>div {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        background-color: tomato;
        aspect-ratio: 1/1;
      }
      <div id="container">
      </div>

      【讨论】:

      • Yeah, was hoping for a CSS-only solution.
      猜你喜欢
      • 2022-12-02
      • 2022-12-01
      • 2020-10-15
      • 1970-01-01
      • 1970-01-01
      • 2013-04-23
      • 2022-12-27
      • 2017-08-18
      • 2021-07-09
      相关资源
      最近更新 更多