117

Is it possible to select a specific grid column or row with CSS?

For example, say I have a 3 row by 2 column CSS grid layout: grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;. How would I select all elements from the 2nd column? For example: grid:nth-child(column:2) (just my idea, not valid code).

I have tried nth-child selectors on the div elements, but this does not allow me to specify row or column when the items are automatically placed by the CSS grid layout engine.

body {
  display: grid;
  grid-template-rows: 1fr 1fr 1fr;
  grid-template-columns: 1fr 1fr;
  grid-gap: 10px;
}

.item {
  background: #999;
}
<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>

<div class="item">
  <p>Right Justify</p>
  <p>Element 1 | Element 2</p>
</div>

<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>

<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>
<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>
<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>
<div class="item">
  <p>Customer Name</p>
  <p>Element 1 | Element 2</p>
</div>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Diode Dan
  • 4,801
  • 6
  • 25
  • 34

7 Answers7

91

To style an arbitrary row, you could use a wrapper element with its display set to contents. See the code snippet below:

.grid-container {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-gap: 2px;
}

.grid-item {
  border: 1px solid black;
  padding: 5px;
}

.grid-row-wrapper {
  display: contents;
}

.grid-row-wrapper > .grid-item {
  background: skyblue;
}
<div class="grid-container">
  <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-item">5</div>
  <div class="grid-row-wrapper">
    <div class="grid-item">6</div>
    <div class="grid-item">7</div>
    <div class="grid-item">8</div>
    <div class="grid-item">9</div>
    <div class="grid-item">10</div>
  </div>
  <div class="grid-item">11</div>
  <div class="grid-item">12</div>
  <div class="grid-item">13</div>
  <div class="grid-item">14</div>
  <div class="grid-item">15</div>
  <div class="grid-item">16</div>
  <div class="grid-item">17</div>
  <div class="grid-item">18</div>
  <div class="grid-item">19</div>
  <div class="grid-item">20</div>
</div>

As with all implementations, you should check to ensure it works in your target environment(s). You can check the compatibility table on MDN or Can I use for support for display: contents:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
chipit24
  • 6,509
  • 7
  • 47
  • 67
  • 4
    This should be the correct answer. The browser support is not perfect but `display: contents;` is made to solve this issue without hack. It's great for DOM stepping for styling the correct row, but at the same time being invisible and skipped from the grid rendering (it does not mess anything up). – Jens Törnell Jun 10 '19 at 07:46
  • 4
    Very nice solution but browser support is still sorely lacking as of Jan 2021 – Julian Suggate Jan 26 '21 at 05:38
  • 6
    No, this is NOT a valid answer. Styling rows and columns with static grid is never a problem since you always know where exactly each element will occur. The question that was initially asked is how to target specific items that were placed on particular row/column automatically by dynamic grid. – Mesqalito May 12 '22 at 15:49
  • Yes, this IS a valid answer! You can add the `grid-row-wrapper` element dynamically. – chipit24 May 16 '22 at 17:52
  • 2
    This wouldn't work with a responsive design, if you changed the number of columns with a media query. If for example you went from 5 columns to 3 on a smaller screen the styles would bleed over to another row. – Christian Pavilonis Sep 09 '22 at 18:02
  • great for rows but breaks when targeting columns. It was a nice try for an answer though and I'm sure useful for other solutions. – Joshua Robison Jun 12 '23 at 04:52
  • This is not an answer in 2023. If we leave this question in this state then we will see people begin to duplicate this question over and over again looking for a solution in 2023 and beyond. When this question was asked, there was no solution but now in 2023 a solution may chance to exist. – Joshua Robison Jun 12 '23 at 05:04
  • This is an answer, and it's the top rated one currently so there really is no doubt about how useful others find it. If you have a solution of your own, feel free to post it by pressing "Add Another Answer" below. – chipit24 Jun 12 '23 at 18:25
50

There aren't any column or row elements that you can target, but if the grid is uniform (same number of cells in each row), you can select cells. Here are some examples.

1. Columns

Last column in a 5-column grid:

.item:nth-child(5n) { /* ... */ }

Fourth (second last) column in a 5-column grid:

.item:nth-child(5n-1) { /* ... */ }

First (5th last) column in a 5-column grid:

.item:nth-child(5n-4) { /* ... */ }

2. Rows

First row in a 5-column grid (first five cells):

.item:nth-child(-n+5) { /* ... */ }

Second row in a 5-column grid (cells from 6 to 10):

.item:nth-child(n+6):nth-child(-n+10) { /* ... */ }

Third row in a 5-column grid (cells from 11 to 15):

.item:nth-child(n+11):nth-child(-n+15) { /* ... */ }

Last row in a 5-column grid with 20 cells (cells from 16 onward):

.item:nth-child(n+16) { /* ... */ }
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mateusz
  • 2,340
  • 25
  • 24
  • 2
    works statically but not dynamically – Royer Adames Jul 05 '22 at 20:42
  • to make it dynamic you can add a class when the group of elements you want is looping through. For example, in Angular, you can use the *ngFor loop odd or even to give a dynamic class to your row of cells and then select them and style them – Royer Adames Jul 05 '22 at 21:29
  • I don't think this works if you have a cell or cells that span multiple columns – JeffreyPia Oct 24 '22 at 15:54
42

It is not possible with CSS.

CSS targets HTML elements, attributes and attribute values.

Grid columns and rows have none of these "hooks".

You'll have to target the grid items directly.

You wrote:

For example, say I have a 3 row by 2 column CSS Grid Layout: grid-template-rows: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr;. How would I select all elements from the 2nd column?

grid-container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  grid-gap: 10px;
  padding: 10px;
  height: 50vh;
  background-color: gray;
}

grid-item {
  background-color: lightgreen;
}

grid-item:nth-child(2n) {
  border: 2px dashed red;
}
<grid-container>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
</grid-container>
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
8

You can not because there are no such selectors.

But that is strange, because you can easily target the row/column from CSS:

#item3 {
  background-color: blue;
  grid-row: span 2 / 7;
}

Thus it is natural to expect something like:

div[style*="display:grid"]:grid-row(3) {
    background-color: blue;
}

div[style*="display:grid"]:grid-column(3) {
    background-color: green;
}

I do not know the reasons that a draft for this has not been proposed yet.

There is a CSSWG draft for column selection

There is also an issue request at the W3C repository

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Eugen Konkov
  • 22,193
  • 17
  • 108
  • 158
6

In the near future we will be able to do it thanks to the Grid-Structural Selectors

The double-association of a cell in a 2D grid (to its row and column) cannot be represented by parentage in a hierarchical markup language. Only one of those associations can be represented hierarchically: the other must be explicitly or implicitly defined in the document language semantics. In both HTML and DocBook, two of the most common hierarchical markup languages, the markup is row-primary (that is, the row associations are represented hierarchically); the columns must be implied. To be able to represent such implied column-based relationships, the column combinator and the :nth-col() and :nth-last-col() pseudo-classes are defined. In a column-primary format, these pseudo-classes match against row associations instead.

The one you need here is :nth-col() that behave the same way as :nth-child()

The :nth-col(An+B) pseudo-class notation represents a cell element belonging to a column that has An+B-1 columns before it ref

Temani Afif
  • 245,468
  • 26
  • 309
  • 415
5

If you ever want to style a row the same principle applies. Taking that example from above:

grid-container {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr 1fr;
  grid-gap: 10px;
  padding: 10px;
  height: 50vh;
  background-color: gray;
}

grid-item {
  background-color: lightgreen;
}

grid-item:nth-child(4n+3),grid-item:nth-child(4n) {
  border: 2px dashed red;
}
<grid-container>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
  <grid-item></grid-item>
</grid-container>
BobRodes
  • 5,990
  • 2
  • 24
  • 26
Domenik Reitzner
  • 1,583
  • 1
  • 12
  • 21
  • 2
    This "solution" is coupled to the number of items and also does not take into account any line-breaks (AKA *wrap*), therefor it is not so useful. – vsync Aug 12 '18 at 16:34
  • @vsync: This is not flexbox, there is no wrap/line break. In grid you have a fixed layout and can apply a fixed styling to those items. – Domenik Reitzner Aug 13 '18 at 18:15
  • but you can't for example style a background-image that spans a entire row, which you can in css display: table – James Cat Oct 26 '20 at 09:53
0

I'll add another answer, it's based on dynamically updating css rules. Unfortunately, we have to resort to this, since there is no way to set nth-child via css variables.

const data =[{"Name":"John","Age":25,"Gender":"Male"},{"Name":"Emily","Age":33,"Gender":"Female"},{"Name":"Michael","Age":42,"Gender":"Male"},{"Name":"Sarah","Age":29,"Gender":"Female"},{"Name":"William","Age":37,"Gender":"Male"},{"Name":"Samantha","Age":26,"Gender":"Female"},{"Name":"Benjamin","Age":48,"Gender":"Male"},{"Name":"Elizabeth","Age":31,"Gender":"Female"},{"Name":"David","Age":45,"Gender":"Male"},{"Name":"Olivia","Age":28,"Gender":"Female"}]


const colors = ['255,0,0', '0,255,0', '0,0,255']
const COLUMNS = 3
let rows = 2
const wp = document.querySelector('.wrapper')
let lastSelectedColumn = null

document.querySelector('button').onclick = function(){
  if (rows > data.length -1) return
  Object.values(data[rows++])
    .map(e => Object.assign(document.createElement('div'),{textContent: e}))
    .forEach(e => wp.appendChild(e)) 
  selectColumn(lastSelectedColumn)
}

wp.onclick = function({target}){
  if (target.className !== "") return
  const index = Array.from(wp.children).indexOf(target)
  selectColumn(lastSelectedColumn = index % COLUMNS)// console.log(lastSelectedColumn)
}

function getRules(column, columns, rows, color){
  const index = (column + 1) % columns 
  const i = index  ? `+ ${index}` : ''
  return [
    `.wrapper div:nth-child(${columns}n${i}){
      border-width: 0px 3px 0 3px; 
      border-color: rgb(${color}); 
      border-style: solid;
      background: rgba(${color},.1);
    }`, /* top cell */
    `.wrapper div:nth-child(${column + 1}) {
      border-width: 3px 3px 0 3px; 
      /* font-weight: bold; */
    }`, /* bottom cell */
    `.wrapper div:nth-child(${(rows-1) * columns  + column + 1}) {
      border-width: 0 3px 3px 3px; 
    }`
  ]
}

function cleanupRules(stylesheet){
  let newRuleIndex = stylesheet.sheet.cssRules.length
  for (; newRuleIndex > 2; newRuleIndex--)
     stylesheet.sheet.deleteRule(newRuleIndex - 1)
  return newRuleIndex 
}

function selectColumn(column){
  const stylesheet = document
    .querySelector('style[title="grid-wrapper"]')
  if (!stylesheet || column === null) return
  const rules = getRules(column, COLUMNS,
    wp.children.length / COLUMNS, colors[column])
  let newRuleIndex = cleanupRules(stylesheet)
  for (const rule of rules){
    stylesheet.sheet.insertRule(rule, newRuleIndex++)
  } // console.log(stylesheet.sheet.cssRules)
}
<style title="grid-wrapper">
  .wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    width: fit-content;
    background: #e5e5e5;
    color: black;
    margin: 10px;
  }
  .wrapper div {
    padding: 5px;
    box-sizing:content-box;
    box-shadow: 1px 1px 1px  black;
  }
</style>  
<p>Please, select a column, and click on `Add`</p>
<div class="wrapper">
  <div>Name</div>
  <div>Age</div>
  <div>Gender</div>
  <div>John</div>
  <div>25</div>
  <div>Male</div>
  <div>Emily</div>
  <div>33</div>
  <div>Female</div>
</div>
<button>Add</button>

UPDATE

Another way is rewrite only selectors in exists CSS rules (without adding or removing them):

const wp = document.querySelector('.wrapper')
const config = {
  data :[{"Name":"John","Age":25,"Gender":"Male"},{"Name":"Emily","Age":33,"Gender":"Female"},{"Name":"Michael","Age":42,"Gender":"Male"},{"Name":"Sarah","Age":29,"Gender":"Female"},{"Name":"William","Age":37,"Gender":"Male"},{"Name":"Samantha","Age":26,"Gender":"Female"},{"Name":"Benjamin","Age":48,"Gender":"Male"},{"Name":"Elizabeth","Age":31,"Gender":"Female"},{"Name":"David","Age":45,"Gender":"Male"},{"Name":"Olivia","Age":28,"Gender":"Female"}],
  colors : ['255,0,0', '0,255,0', '0,0,255'],
  cols : 3,
  rows : 3,
  lastSelectedColumn: null
}

function getSelectorTexts(column, columns, rows, color){
  const index = (column + 1) % columns 
  const i = index  ? `+ ${index}` : ''
  return [
    `.wrapper div:nth-child(${columns}n${i})`, 
    `.wrapper div:nth-child(${column + 1})`, 
    `.wrapper div:nth-child(${(rows-1) * columns  + column + 1})`
  ]
}

function selectColumn(column){
  const stylesheet = document
    .querySelector('style[title="grid-wrapper"]')
  if (!stylesheet || column === null) return
  const rules = getSelectorTexts(column, config.cols,
     wp.children.length / config.cols, config.colors[column])
  let i = 2  
  for (const rule of rules){
    stylesheet.sheet.rules[i++].selectorText = rule
  }
}

document.querySelector('button').onclick = function(){
  if (config.rows > config.data.length -1) return
  Object.values(config.data[config.rows++])
    .map(e => Object.assign(document.createElement('div'),{textContent: e}))
    .forEach(e => wp.appendChild(e)) 
  selectColumn(config.lastSelectedColumn)
}

wp.onclick = function({target}){
  if (target.className !== "") return
  const index = Array.from(wp.children).indexOf(target)
  selectColumn(config.lastSelectedColumn = index % config.cols)
}
<style title="grid-wrapper">
  .wrapper {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    width: fit-content;
    background: #e5e5e5;
    color: black;
    margin: 10px;
  }
  .wrapper div {
    padding: 5px;
    border-width: 3px;
    border-color: transparent; 
    box-shadow: 1px 1px 1px  black;
  }

  .rule1 {
    border-width: 0px 3px 0 3px; 
    border-color: rgb(0, 0, 255);
    border-style: solid;
    background: rgba(0, 0, 255, .1);
  }

  .rule2 {
    border-width: 3px 3px 0 3px;
    border-color: rgb(0, 0, 255);
  }

  .rule2 {
    border-width: 0 3px 3px 3px; 
    border-color: rgb(0, 0, 255);
  }
</style>   
<p>Please, select a column, and click on `Add`</p>
<div class="wrapper">
  <div>Name</div>
  <div>Age</div>
  <div>Gender</div>
  <div>John</div>
  <div>25</div>
  <div>Male</div>
  <div>Emily</div>
  <div>33</div>
  <div>Female</div>
  <div>Michael</div>
  <div>42</div>
  <div>Male</div>
</div>
<button>Add</button>
Daniil Loban
  • 4,165
  • 1
  • 14
  • 20