11

Let's say I have a 3-column CSS-Grid. Is there a way, using JavaScript, to get the grid-row and grid-column of an auto-placed element?

Example:

console.log($('#test').css('grid-row'), $('#test').css('grid-column'));
// expected output: 2 3 
// actual output: two empty strings
.grid {
  display: grid;
  grid-template-columns: repeat( 3, 1fr);
}
<div class="grid">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div id="test"></div>
  <div></div>
  <div></div>
  <div></div>
</div>

Here is a JSFiddle of the example: https://jsfiddle.net/w4u87d2f/7/

In this example I could figure it out by counting the elements and knowing the grid has three columns:

grid-column = $('#test').index() % 3 + 1;
grid-row = Math.ceil( $('#test').index() / 3 )

But that only works for very simple grids and also means I have to consider breakpoints that change the number of columns.

Edit: This is not a duplicate of Retrieve the position (X,Y) of an HTML element, as I'm not interested in pixel coordinates but the row and column number within the CSS-Grid.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Seraphithan
  • 399
  • 1
  • 4
  • 14
  • How would you handle mobile? – Rainb Jul 13 '18 at 14:59
  • 1
    Why not just put that into the `id` and make life easy? – James Gould Jul 13 '18 at 15:13
  • @RyanWilson I have added a fiddle that shows the example with a bit of added CSS to give the divs dimensions and colour. – Seraphithan Jul 13 '18 at 15:13
  • @JayGould because in the task I'm trying to solve I don't have an `id` and the row number will be depended on the breakpoint. – Seraphithan Jul 13 '18 at 15:16
  • @Rainb I'm not sure what you mean, how would I handle what on mobile? – Seraphithan Jul 13 '18 at 15:18
  • @Seraphithan Will there always be a parent container div with class = grid? Also, in your example you are using JQuery, if an answer can utilize JQuery, please add that as a tag to your post. – Ryan Wilson Jul 13 '18 at 15:47
  • @RyanWilson yes the parent will always have the class grid. And thanks for the tag advice. – Seraphithan Jul 13 '18 at 15:58
  • @Seraphithan I think I may have a solution for you, I will post it as an answer. How will you be identifying the elements position inside the div with class = grid, will it get a class attribute added when clicking it, or what is the workflow for that?? – Ryan Wilson Jul 13 '18 at 16:01
  • @Seraphithan In other words, how will you know which div inside div = grid you are targeting and wanting to know its position? – Ryan Wilson Jul 13 '18 at 16:07
  • Does this answer your question? [How to find in which column a CSS grid layout element is placed using JavaScript](https://stackoverflow.com/questions/45870156/how-to-find-in-which-column-a-css-grid-layout-element-is-placed-using-javascript) – clickbait Mar 22 '22 at 02:59

3 Answers3

9

The above answer is a great start, and uses jQuery. Here is a pure Javascript equivalent, and also implements an "offset" in case you have specified the first child element's grid column (such as in a calendar where you specify the first day of the month)

function getGridElementsPosition(index) {
  const gridEl = document.getElementById("grid");

  // our indexes are zero-based but gridColumns are 1-based, so subtract 1
  let offset = Number(window.getComputedStyle(gridEl.children[0]).gridColumnStart) - 1; 

  // if we haven't specified the first child's grid column, then there is no offset
  if (isNaN(offset)) {
    offset = 0;
  }
  const colCount = window.getComputedStyle(gridEl).gridTemplateColumns.split(" ").length;

  const rowPosition = Math.floor((index + offset) / colCount);
  const colPosition = (index + offset) % colCount;

  //Return an object with properties row and column
  return { row: rowPosition, column: colPosition };
}

function getNodeIndex(elm) {
  var c = elm.parentNode.children,
    i = 0;
  for (; i < c.length; i++) if (c[i] == elm) return i;
}

function addClickEventsToGridItems() {
  let gridItems = document.getElementsByClassName("grid-item");
  for (let i = 0; i < gridItems.length; i++) {
    gridItems[i].onclick = (e) => {
      let position = getGridElementsPosition(getNodeIndex(e.target));
      console.log(`Node position is row ${position.row}, column ${position.column}`);
    };
  }
}

addClickEventsToGridItems();

Here is a corresponding Pen that shows it in action on a calendar with a specified offset.

sorrell
  • 1,801
  • 1
  • 16
  • 27
  • 2
    Thank you for the pure JS version. Didn't know about `window.getComputedStyle()` before and it really is a game changer here. – Elpy Jan 07 '22 at 17:32
7
//Add click event for any child div of div = grid
$(document).ready(function(){
    $('.grid').on('click', 'div', function(e){
          GetGridElementsPosition($(this).index()); //Pass in the index of the clicked div
    //Relevant to its siblings, in other words if this is the 5th div in the div = grid
    });
});

function GetGridElementsPosition(index){
    //Get the css attribute grid-template-columns from the css of class grid
    //split on whitespace and get the length, this will give you how many columns
    const colCount = $('.grid').css('grid-template-columns').split(' ').length;

    const rowPosition = Math.floor(index / colCount);
    const colPosition = index % colCount;

    //Return an object with properties row and column
    return { row: rowPosition, column: colPosition } ;
}
Ryan Wilson
  • 10,223
  • 2
  • 21
  • 40
  • @Seraphithan If this isn't what you are looking for, let me know and I'll remove this. – Ryan Wilson Jul 13 '18 at 17:28
  • This looks great, thanks and I think I can even adapt it to work with IE 11 grid. Hopefully there will be a direct way to access these parameters eventually, because I think this would break if an element spanned more than one row or column, but luckily that's not an issue with my task. – Seraphithan Jul 13 '18 at 18:05
  • @seraphithan yeah, if an element can span multiple columns or rows, I'd have to see an example and it may be possible to modify this, glad it helps you. – Ryan Wilson Jul 14 '18 at 15:06
0

Here another solution taking into considerations columns spans

function getGridPosition(elem) {
    var gridContainer = elem.parent();
    var simpleEl = elem.get(0);
    var gridItems = gridContainer.children('div');
    const colCount = $(gridContainer).css('grid-template-columns').split(' ').length;

    var row = 0;
    var col = 0;

    gridItems.each(function(index,el) {

        var item = $(el);
        if(simpleEl==el) {
            //console.log("FOUND!")
            return false;
        }
        var gridCols  = item.css("grid-column");
        if(gridCols != undefined && gridCols.indexOf("span")>=0){
            var gridColumnParts = gridCols.split('/');
            var spanValue = parseInt(gridColumnParts[0].trim().split(' ')[1], 10);
            //console.log("spanValue: " + spanValue);
            col = col+spanValue;
        }else{
            col++;
        }
        if(col>=colCount){
            col=0;
            row++;
        }
    });

    return {
        row: row,
        col: col
    };
}
Neo
  • 1,337
  • 4
  • 21
  • 50