CSS Grid – Highlight cells up to the hovered cell
In a grid, I want to highlight group of cells – a rectangle shape – starting from top left cell up to the cell under the mouse position.
Let’s say our grid initially looks like this:
.grid { display: grid; grid-template-columns: repeat(5, 50px); grid-template-rows: repeat(5, 50px); gap: 5px; } .grid-item { display: flex; justify-content: center; align-items: center; background: lightgray; }
<div class="grid"> <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-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 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 class="grid-item">21</div> <div class="grid-item">22</div> <div class="grid-item">23</div> <div class="grid-item">24</div> <div class="grid-item">25</div> </div>
And now the user hovers with the mouse over cell number 18. The grid should look like this now:
.grid { display: grid; grid-template-columns: repeat(5, 50px); grid-template-rows: repeat(5, 50px); gap: 5px; } .grid-item { display: flex; justify-content: center; align-items: center; background: lightgray; } .grid-item-blue { background: lightblue; }
<div class="grid"> <div class="grid-item grid-item-blue">1</div> <div class="grid-item grid-item-blue">2</div> <div class="grid-item grid-item-blue">3</div> <div class="grid-item">4</div> <div class="grid-item">5</div> <div class="grid-item grid-item-blue">6</div> <div class="grid-item grid-item-blue">7</div> <div class="grid-item grid-item-blue">8</div> <div class="grid-item">9</div> <div class="grid-item">10</div> <div class="grid-item grid-item-blue">11</div> <div class="grid-item grid-item-blue">12</div> <div class="grid-item grid-item-blue">13</div> <div class="grid-item">14</div> <div class="grid-item">15</div> <div class="grid-item grid-item-blue">16</div> <div class="grid-item grid-item-blue">17</div> <div class="grid-item grid-item-blue">18</div> <div class="grid-item">19</div> <div class="grid-item">20</div> <div class="grid-item">21</div> <div class="grid-item">22</div> <div class="grid-item">23</div> <div class="grid-item">24</div> <div class="grid-item">25</div> </div>
I prefer css solution. Is is possible using css only? If not, how would you do this in JS?
Here is pure CSS solution with flexbox but too complex.
.grid { display: flex; flex-wrap: wrap; flex-flow: wrap-reverse; direction: rtl; width: calc(50px*5 + 5px*4); } .grid-item { width: 50px; height: 50px; display: flex; justify-content: center; align-items: center; background: lightgray; margin-bottom: 5px; } .grid-item:not(:nth-child(5n + 1)) { margin-right: 5px; } .grid-item:nth-child(5n - 4):hover, .grid-item:nth-child(5n - 4):hover~.grid-item, .grid-item:nth-child(5n - 3):hover, .grid-item:nth-child(5n - 3):hover~.grid-item:not(:nth-child(5n + 1)), .grid-item:nth-child(5n - 2):hover, .grid-item:nth-child(5n - 2):hover~.grid-item:not(:nth-child(5n + 1)):not(:nth-child(5n + 2)), .grid-item:nth-child(5n - 1):hover, .grid-item:nth-child(5n - 1):hover~.grid-item:not(:nth-child(5n + 1)):not(:nth-child(5n + 2)):not(:nth-child(5n + 3)), .grid-item:nth-child(5n):hover, .grid-item:nth-child(5n):hover~.grid-item:not(:nth-child(5n + 1)):not(:nth-child(5n + 2)):not(:nth-child(5n + 3)):not(:nth-child(5n + 4)) { background: lightblue; }
<div class="grid"> <div class="grid-item">25</div> <div class="grid-item">24</div> <div class="grid-item">23</div> <div class="grid-item">22</div> <div class="grid-item">21</div> <div class="grid-item">20</div> <div class="grid-item">19</div> <div class="grid-item">18</div> <div class="grid-item">17</div> <div class="grid-item">16</div> <div class="grid-item">15</div> <div class="grid-item">14</div> <div class="grid-item">13</div> <div class="grid-item">12</div> <div class="grid-item">11</div> <div class="grid-item">10</div> <div class="grid-item">9</div> <div class="grid-item">8</div> <div class="grid-item">7</div> <div class="grid-item">6</div> <div class="grid-item">5</div> <div class="grid-item">4</div> <div class="grid-item">3</div> <div class="grid-item">2</div> <div class="grid-item">1</div> </div>
I think it very hard to do this with pure CSS, especially if you plan to expand this grid. However it can be done with javascript
document.querySelector('.grid').addEventListener("mouseover", function (event) { Array.from(this.querySelectorAll('.grid-item')).forEach(node => { var nodePosition = node.getBoundingClientRect(); if (nodePosition.x <= event.clientX && nodePosition.y <= event.clientY) { node.classList.add('grid-item-blue') } else { node.classList.remove('grid-item-blue') } }) });
.grid { display: grid; grid-template-columns: repeat(5, 50px); grid-template-rows: repeat(5, 50px); gap: 5px; } .grid-item { display: flex; justify-content: center; align-items: center; background: lightgray; } .grid-item-blue { background: lightblue; }
<!DOCTYPE html> <html lang="en"> <head> </head> <body> <div class="grid"> <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-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 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 class="grid-item">21</div> <div class="grid-item">22</div> <div class="grid-item">23</div> <div class="grid-item">24</div> <div class="grid-item">25</div> </div> </body>
You can also use debouncing to reduce the number of times the mouseover
callback function is triggered.