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?

Add Comment
2 Answer(s)

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>

Add Comment

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.

Answered on July 15, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.