Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to directly access the actual grid coordinates of an auto-placed element in CSS grid?

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?

like image 395
Amanda Harvey Avatar asked Mar 11 '19 19:03

Amanda Harvey


1 Answers

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>
like image 162
clickbait Avatar answered Oct 17 '22 04:10

clickbait