16

Someone else asked a similar question here but the answer only works if every element on the page is auto-placed and takes up only one column.

I have a 12-column grid layout in which the first three rows are all taken up by one element that spans all twelve columns; then I have three rows of auto-placed elements that each take up six columns.

While I could do the math to work this out based on that previous answer, it seems somewhat absurd to think that the calculated placement of the element isn't stored anywhere that is accessible by Javascript.

If I access the computed CSS properties regarding the grid, I'm just getting auto and span 6, rather than the actual computed number of row-start or row-end. Is there really no way to access the computed value of grid-row and grid-column?

Roy
  • 7,811
  • 4
  • 24
  • 47
Amanda Harvey
  • 529
  • 5
  • 24
  • 2
    Is there some reason `Element.getBoundingClientRect()` doesn't do the job? https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect – Randy Casburn Mar 11 '19 at 19:22
  • 1
    @RandyCasburn My understanding is that it returns values in pixels, whereas I want the value in terms of rows and columns. My goal in this particular case is to determine which row a clicked element is on at the time of the click, so I can adjust its size without changing its row. – Amanda Harvey Mar 11 '19 at 19:24
  • Forgive the confusion here, but grid-row and grid-column are definitive. You defined them: 12-column 3-row, 6-column 1-row. These don't change. 1fr, 2fr, 3fr all are consistent measurements regardless of viewport size. So since all these column/row definitions are fixed, the only thing that changes is the pixel mesures of each of the block level elements. Regardless of size, a 6-column 1-row element will always be a 6-column 1-row element. Help me become unconfused. – Randy Casburn Mar 11 '19 at 21:55
  • Ah, sorry, I'm not being clear-- I've defined the size of the grid, but not the position of all of the elements in the grid. The elements are always 6-column, 1-row, but the row that they're in (and the column that they begin in) is not explicitly assigned by me. I want to figure out which row they've been assigned to by the auto-placement algorithm. – Amanda Harvey Mar 12 '19 at 15:08
  • 1
    Went back and re-read the spec. It doesn't appear there is a non-computational solution for your use case. – Randy Casburn Mar 12 '19 at 15:22
  • 2
    Could you post the source html and css? – Winston de Greef Mar 14 '22 at 14:06
  • Possible duplicate of https://stackoverflow.com/questions/61964280/is-there-a-way-to-detect-which-css-grid-column-and-row-an-element-is-in-using-ja – clickbait Mar 22 '22 at 03:21

1 Answers1

1

I couldn’t find a way to retrieve grid coordinates directly.

Here’s a function that gets the grid coordinates of an element that’s inside a grid. It works for elements that take up more than 1 column or more than 1 row.

function getItemGridCoords(element) {

  function digitsAndSpacesOnly(string) {
    return string.replace(/[^\d\s.]/g, '');
  }

  function parseTemplate(gridTemplate) {
 // parses a CSS grid-template-[row|column] to Numbers and does cumulative summation
 // "100px 100px 100px" → [100,100,100] → [100,200,300]

    return digitsAndSpacesOnly(gridTemplate).split(/\s/gmi).map(Number).reduce((array,currentValue) => {
      array.push(currentValue + array[array.length-1]);
      return array;
    },[0]);
  }
  
  function findIndexOfClosest(array,target) {
    return array.reduce((prev,curr,indexCurr) => {
      return (Math.abs(curr - target) < Math.abs(array[prev] - target) ? indexCurr : prev);
    });
  }
  
  const grid = element.parentElement;
  const computedStyles = getComputedStyle(grid);
  const getGapsAdder = gap => (n,i) => n + i * gap;
  const colsPositions = parseTemplate(computedStyles['grid-template-columns']).map(getGapsAdder(digitsAndSpacesOnly(computedStyles['column-gap'])));
  const rowsPositions = parseTemplate(computedStyles['grid-template-rows']).map(getGapsAdder(digitsAndSpacesOnly(computedStyles['row-gap'])));
  
  const bounds = element.getBoundingClientRect();
  const gridBounds = grid.getBoundingClientRect();
  
  return {
    col : findIndexOfClosest(colsPositions, bounds.left - gridBounds.left),
    row : findIndexOfClosest(rowsPositions, bounds.top - gridBounds.top),
  };
}





for (let element of document.querySelectorAll('.grid > *')) {
  element.innerHTML = JSON.stringify(getItemGridCoords(element));
}
.grid {
  font-family: monospace;
  display: grid;
  grid : auto-flow dense / repeat(12,1fr);
  gap: 0.5em;
}

.grid > * {
  white-space: nowrap;
  background: aliceblue;
  min-height: 2.5em;
}

.whole {
  grid-column: auto / span 12;
}
.two-thirds {
  grid-column: auto / span 8;
}
.half {
  grid-column: auto / span 6;
}
.third {
  grid-column: auto / span 4;
}

.skyscraper {
  grid-row: auto / span 3;
}
<div class='grid'>
 <span class='whole'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
</div>

<hr>

<div class='grid'>
 <span class='third'></span>
 <span class='third'></span>
 <span class='third'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
 <span class='half'></span>
</div>

<hr>

<div class='grid'>
 <span class='third skyscraper'></span>
 <span class='third'></span>
 <span class='third'></span>
 <span class='two-thirds'></span>
 <span class='third'></span>
 <span class='third'></span>
 <span class='half'></span>
 <span class='half'></span>
</div>
clickbait
  • 2,818
  • 1
  • 25
  • 61