7

Consider a CSS grid where rows can have variable heights:

.grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-column-gap: 16px;
  grid-row-gap: 8px;
}

.first {
  grid-column: 1 / 3;
  background-color: #ccc;
}

.second {
  grid-column: 5 / 6;
  grid-row: 2 / 5;
  background-color: #ccc;
  height: 120px;
}
<div class="grid">
  <div class="first">First</div>
  <div class="second">Second</div>
</div>

When hovering on the grid element in DevTools, Chrome visualizes the grid like this:

enter image description here

How could I achieve a similar grid overlay effect using CSS (or JavaScript, if needed)?

Notes:

  1. All grid cells should be highlighted, even if grid items don't occupy them.
  2. Grid cells can have variable heights (in the example above the first row height is smaller than the rest of the rows).
3
  • css-tricks.com/building-css-grid-overlay Commented Dec 6, 2019 at 2:26
  • @Michael_B That solution doesn't seem to support variable row heights. Commented Dec 6, 2019 at 2:52
  • 2
    Right. It's not an easy request because the Dev Tool grid overlay highlights the structure of the grid. CSS doesn't deal in the abstract; it styles actual elements. Commented Dec 6, 2019 at 2:56

2 Answers 2

8

One idea using JS is to read the computed value of grid-template-columns and grid-template-rows in order to create another grid above the one you have filled with placeholder elements.

Here is a basic example. You should update the values on hover since getComputedStyle will return pixel values:

var grid = document.querySelector('.grid');
var overlay = document.createElement("div");
overlay.className = 'overlay';
overlay.style.gridTemplateRows = window.getComputedStyle(grid, null).getPropertyValue("grid-template-rows");
overlay.style.gridTemplateColumns = window.getComputedStyle(grid, null).getPropertyValue("grid-template-columns");
grid.appendChild(overlay);

/* Get the number of items*/
var Nc = overlay.style.gridTemplateColumns.split(" ").length;
var Nr = overlay.style.gridTemplateRows.split(" ").length;
/* Create placeholder items*/
for (var i = 0; i < Nc * Nr; i++) {
  var d = document.createElement("div");
  overlay.appendChild(d);
}

/* Update the values on hover*/
grid.addEventListener('mouseover', function() {
  overlay.style.gridTemplateRows = window.getComputedStyle(grid, null).getPropertyValue("grid-template-rows");
  overlay.style.gridTemplateColumns = window.getComputedStyle(grid, null).getPropertyValue("grid-template-columns");
})
.grid {
  display: grid;
  grid-template-columns: repeat(8, 1fr);
  grid-column-gap: 16px;
  grid-row-gap: 8px;
  position: relative;
  overflow:hidden;
}

.first {
  grid-column: 1 / 3;
  background-color: #ccc;
}

.second {
  grid-column: 5 / 6;
  grid-row: 2 / 5;
  background-color: #ccc;
  height: 120px;
}

.overlay {
  position: absolute;
  display: grid;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  grid-gap: inherit;
  opacity: 0;
  pointer-events: none;
}

.overlay>* {
  border: 1px dotted;
  background: rgba(0, 125, 0, 0.4);
}

.grid:hover .overlay {
  opacity: 1;
}
<div class="grid">
  <div class="first">First</div>
  <div class="second">Second</div>
</div>

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for this! Could you explain what you mean by "You should update the values on hover since getComputedStyle will return pixel values"?
@MishaMoroshko getComputedStyle will return the actual value based on screen size .. for example if you define a template using 1fr you will not get 1fr but the pixel value of 1fr considering your actual screen size so if you change the size of the screen you need to get the new value again .. You can inspect the element to see that the templates are defined using pixel values. I said you need to change this on hover but you can also do it on screen resize but it's better on hover so you do the cacluation only when needed.
Assuming that the number of rows and columns won't change, we could give overlay grid items a grid-row and a grid-column instead of setting grid-template-rows and grid-template-columns on the grid. That way you don't need to recalculate on window resize, right?
@MishaMoroshko no, setting grid-row/column is not needed if you have the correct number of items because they will placed automatically to fill all the grid cells. You will always need to get the value of grid-template-* but in case you know the template you can use inherit . Example: jsfiddle.net/Ltavbncr/2 .. here I consider the fact that you defined the template columns to have 8 element so I can inherit this and I don't need to calculate it but I need to do it for the row because it's dymanic and based on your elements
Thanks, makes sense! Minor question: what's the importance of overflow: hidden on the grid?
|
1

Create elements for each grid cell:

const grid = document.getElementsByClassName("grid")[0];

const rows = 4;
const cols = 8;

for (let r = 1; r <= rows; r++) {
    for (let c = 1; c <= cols; c++) {
        const h = document.createElement("div");
        h.classList.add("highlight");
        h.style.gridRow = r;
        h.style.gridColumn = c;
        grid.appendChild(h);
    }
}

(This could also be done server side)

And style them:

.highlight {
  z-index: 1;
  border: 1px dashed blue;
  background: #0000FF44;
}

To show them only on hover use:

.highlight {
    display: none;
}

.grid:hover > .highlight {
    display: block;
}

2 Comments

How would this approach work with existing grid items?
It ignores them. Grid display allows multiple elements in a single "cell" without interfering each other. They are draw on top of each other. That's what the z-index is for: to determine the drawing order.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.