Search code examples
csscss-grid

CSS GRID not able to rearrange items with order:;


I’m trying to rearrange the order of a CSS GRID.

But apparently all grid items have a default order value of 0. So order: 1; applied to a grid item would make after everything else, not before.

I want all my grid items to have by default an ascending order. So I can use order: 2; and have the item actually placing on the second place.

Can you help me please?!

Here’s an example, (my actual code is a bit more complicated)

<div class="grid">

<p class="item">1</p>
<p class="item">2</p>
<p class="item two">3</p>
<p class="item">4</p>

</div>
.grid {
  display: grid;
  padding: 30px;
  justify-content: center;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 20px;
}

.two { order: 2; }

I’ve tried doing this, but it’s not working;

.item:nth-child(1) { order:1; }
.item:nth-child(2) { order:2; }
.item:nth-child(3) { order:3; }
.item:nth-child(4) { order:4; }

Fiddle: https://jsfiddle.net/3695vs27/


Solution

  • If you look at the docs on MDN: https://developer.mozilla.org/en-US/docs/Web/CSS/order, you'll see the default value for order is 0 so anything without order explicitly set will be the leading elements, then explicitly set sources apply.

    You could use something like the following.

    .grid {
      display: grid;
      padding: 30px;
      justify-content: center;
      grid-template-columns: repeat(4, 1fr);
      grid-column-gap: 20px;
    }
    
    .item:nth-child(2) { order:3; }
    .item:nth-child(3) { order:2; }
    .item:nth-child(4) { order:4; }
    <div class="grid">
    
    <p class="item">1</p>
    <p class="item">2</p>
    <p class="item two">3</p>
    <p class="item">4</p>
    
    </div>

    Or getting slightly tricky we can use the generic sibling combinator to deal with elements that occur after the swapped elements.

    Edit based on comment

    If we apply the class to the element we want to move up the order instead of the one we want to move down, we can doe this with 1 class and 3 selectors, making use of sibling selectors and a little bit of working with specificity.

    .grid {
      display: grid;
      padding: 30px;
      justify-content: center;
      grid-template-columns: repeat(4, 1fr);
      grid-column-gap: 20px;
    }
    
    /*Swap the elements - now targeting the element we want to move UP*/
    .item.three{ order:3; }
    /*Move the next item sibling down*/
    .item.three + .item { order:2; }
    /*Deal with the elements after the swap with some sibling combinators 
    by targeting all the siblings of the immediate sibling we targeted above*/
    .item.three + .item ~ .item { order:4; }
    <div class="grid">
    
    <p class="item">1</p>
    <p class="item three">2</p>
    <p class="item">3</p>
    <p class="item">4</p>
    <p class="item">5</p>
    <p class="item">6</p>
    <p class="item">7</p>
    
    </div>

    Moving an item DOWN

    We can do it downwards too, it gets a little bit trickier though. Doing this for any given number will be hard as you need to know what element is going where. This is something that is probably beyond the reach of CSS at the moment and you may be better off with some javascript.

    .grid {
      display: grid;
      padding: 30px;
      justify-content: center;
      grid-template-columns: repeat(4, 1fr);
      grid-column-gap: 20px;
    }
    
    /*Target the element to move down*/
    .item.two{ order:2; }
    /*Now we need to know where we are moving the item to and use nth-child again*/
    .item:nth-child(2) { order:3; }
    /*Adjust the remaining siblings, but not the one we've already moved*/
    .item:nth-child(2) ~ .item:not(.two) { order:4; }
    <div class="grid">
    
    <p class="item">1</p>
    <p class="item">2</p>
    <p class="item">3</p>
    <p class="item">4</p>
    <p class="item two">5</p>
    <p class="item">6</p>
    <p class="item">7</p>
    
    </div>

    Going the Javascript route

    To enable arbitrary placement via class we'll use javascript to give each element a native order which our css will override.

    let items = document.querySelectorAll(".grid .item");
    
    for(var i = 0; i < items.length; i++) {
      //Only update items without a modifying class
      if(items[i].classList.length == 1) {
        items[i].style.order=i;
      }
    }
    .grid {
      display: grid;
      padding: 30px;
      justify-content: center;
      grid-template-columns: repeat(4, 1fr);
      grid-column-gap: 20px;
    }
    
    .two {order:2; color:red;}
    .six {order:6; color:green;}
    .ten {order:10; color:blue;}
    <div class="grid">
    
    <p class="item">1</p>
    <p class="item">2</p>
    <p class="item six">3</p>
    <p class="item">4</p>
    <p class="item two">5</p>
    <p class="item">6</p>
    <p class="item">7</p>
    <p class="item">8</p>
    <p class="item">9</p>
    <p class="item">10</p>
    <p class="item">11</p>
    <p class="item">12</p>
    <p class="item">13</p>
    <p class="item">14</p>
    <p class="item ten">15</p>
    <p class="item">16</p>
    
    </div>

    BUT if i was going down the javascript path, i'd skip classes for ordering and use data attributes.

    Data attributes can be styles using the attribute selector. This way your list of elements can be arbitrarily long and you don't have to worry about a CSS class for each insertion point.

    let items = document.querySelectorAll(".grid .item");
    
    for(var i = 0; i < items.length; i++) { 
        //if we have a data attribute use that, otherwise use the natural order
        items[i].style.order= items[i].dataset.order ? items[i].dataset.order :  i;
    }
    .grid {
      display: grid;
      padding: 30px;
      justify-content: center;
      grid-template-columns: repeat(4, 1fr);
      grid-column-gap: 20px;
    }
    
    
    [data-order] {
    font-weight:bold;
    background-color:#EEE;
    }
    <div class="grid">
    
    <p class="item">1</p>
    <p class="item">2</p>
    <p class="item" data-order="6">3</p>
    <p class="item">4</p>
    <p class="item" data-order="2">5</p>
    <p class="item">6</p>
    <p class="item">7</p>
    <p class="item">8</p>
    <p class="item">9</p>
    <p class="item">10</p>
    <p class="item">11</p>
    <p class="item">12</p>
    <p class="item">13</p>
    <p class="item">14</p>
    <p class="item" data-order="10">15</p>
    <p class="item">16</p>
    
    </div>