How to make hover state on row table with CSS grid layout
Asked Answered
D

7

52

This a table that created with CSS Grid Layout, but I have a problem with it, I can't make hover state on each row.

I only want use CSS for this.

Can anyone give me a solution for this?

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
}

.table>* {
  background: gray;
  padding: 10px;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}
<div class="table">
  <div class="heading">Title 1</div>
  <div class="heading">Title 2</div>
  <div class="heading">Title 3</div>
  <div class="heading">Title 4</div>
  <div class="heading">Title 5</div>

  <div class="row">Row 1</div>
  <div class="row">Row 1</div>
  <div class="row">Row 1</div>
  <div class="row">Row 1</div>
  <div class="row">Row 1</div>

  <div class="row">Row 2</div>
  <div class="row">Row 2</div>
  <div class="row">Row 2</div>
  <div class="row">Row 2</div>
  <div class="row">Row 2</div>

  <div class="row">Row 3</div>
  <div class="row">Row 3</div>
  <div class="row">Row 3</div>
  <div class="row">Row 3</div>
  <div class="row">Row 3</div>
</div>
Drupelet answered 5/1, 2018 at 7:14 Comment(0)
D
20

Here is a trick using pseudo-element. The idea is to use the element as a background and make it overflow on the left and right so that it will cover all the row. Like that if you hover on any element inside a row you change the color and it's like you changed the color of the whole row.

This trick involve few changes on the markup and also more CSS.

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
  overflow: hidden;
  background: gray;
}

.table>* {
  padding: 10px;
  position: relative;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}

.row span {
  position: relative;
  z-index: 2;
}

.row:before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1000%;
  left: -1000%;
  z-index: 1;
}

.row:after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 1px;
  z-index: 2;
  background-color: #fff;
}

.row:nth-child(5n+5)::after {
  bottom: -1px;
  right: 0;
  left: -1000%;
  height: 1px;
  z-index: 3;
  width: auto;
  top: auto;
  background-color: #fff;
}

.row:hover::before {
  background-color: red;
}
<div class="table">
  <div class="heading">Title 1</div>
  <div class="heading">Title 2</div>
  <div class="heading">Title 3</div>
  <div class="heading">Title 4</div>
  <div class="heading">Title 5</div>

  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>

  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>

  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
</div>
Dryly answered 5/1, 2018 at 8:3 Comment(6)
Why we need row::after?Drupelet
@NguyenTrungHieu the after is to re-create the border missed by the beforeDryly
@NguyenTrungHieu not weird but logic :) it's because the element are overlowing and with z-index only the last one is triggered. By the way i find a fix to the issue, you can see .. now i only missed some borderDryly
@NguyenTrungHieu well, if you change the structure you will for sure have some issue :) as the solution it not 100% generic since it rely on some hacky trick, so maybe the code need to adjusted. And for the blink simply remive the transition if you don't need it;)Dryly
That's buggy when cell contents' text is long enough to wrap over multiple lines.Dhoti
Just for the record, it does not work with the gaps. So it is more "multiple cells" than a "row" notion.Ferdinand
P
74

Since you are treating this as a table, where the elements stay in the same row, you can also wrap each row in a DIV with "display" set to "contents." This forms an innocuous parent element that you use for hover, and then style the child elements. (display: contents is not yet supported in Edge, though.)

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
  overflow: hidden;
  background: gray;
}

.table .row, .table .heading{
  padding: 10px;
  position: relative;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}

.row span {
  position: relative;
  z-index: 2;
}

.rowWrapper{
  display: contents;
}

.rowWrapper:hover > div{
  background-color: orange;
}
<div class="table">

        <div class="heading">Title 1</div>
        <div class="heading">Title 2</div>
        <div class="heading">Title 3</div>
        <div class="heading">Title 4</div>
        <div class="heading">Title 5</div>
      
    <div class="rowWrapper">
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
        <div class="row"><span>Row 1</span></div>
    </div>

    <div class="rowWrapper">
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
        <div class="row"><span>Row 2</span></div>
    </div>
      
    <div class="rowWrapper">
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
        <div class="row"><span>Row 3</span></div>
    </div>
    
</div>
Peltz answered 6/12, 2018 at 1:27 Comment(3)
This is a neat solution. Note to others who accidentally tried to add background to the rowWrapper itself and got confused because it "doesn't exist": Set background on the children of the rowWrapper.Gaiser
You can only slightly notice it in this example, but the hover does not work on the gap (1px in this case) in Firefox. Works in Chrome.Kone
Also it works only for solid colors hover effect - if you want to add o shadow on the entire row, it isn't possible using this approach.Rastus
D
20

Here is a trick using pseudo-element. The idea is to use the element as a background and make it overflow on the left and right so that it will cover all the row. Like that if you hover on any element inside a row you change the color and it's like you changed the color of the whole row.

This trick involve few changes on the markup and also more CSS.

.table {
  display: grid;
  grid-template-columns: [col-start] auto [col-end];
  grid-template-rows: [header-start] 50px [header-end row-start] auto [row-end];
  grid-auto-rows: auto;
  grid-auto-columns: auto;
  grid-gap: 1px;
  overflow: hidden;
  background: gray;
}

.table>* {
  padding: 10px;
  position: relative;
}

.heading {
  background: navy;
  color: #fff;
  grid-row: header;
}

.row span {
  position: relative;
  z-index: 2;
}

.row:before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1000%;
  left: -1000%;
  z-index: 1;
}

.row:after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -1px;
  width: 1px;
  z-index: 2;
  background-color: #fff;
}

.row:nth-child(5n+5)::after {
  bottom: -1px;
  right: 0;
  left: -1000%;
  height: 1px;
  z-index: 3;
  width: auto;
  top: auto;
  background-color: #fff;
}

.row:hover::before {
  background-color: red;
}
<div class="table">
  <div class="heading">Title 1</div>
  <div class="heading">Title 2</div>
  <div class="heading">Title 3</div>
  <div class="heading">Title 4</div>
  <div class="heading">Title 5</div>

  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>
  <div class="row"><span>Row 1</span></div>

  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>
  <div class="row"><span>Row 2</span></div>

  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
  <div class="row"><span>Row 3</span></div>
</div>
Dryly answered 5/1, 2018 at 8:3 Comment(6)
Why we need row::after?Drupelet
@NguyenTrungHieu the after is to re-create the border missed by the beforeDryly
@NguyenTrungHieu not weird but logic :) it's because the element are overlowing and with z-index only the last one is triggered. By the way i find a fix to the issue, you can see .. now i only missed some borderDryly
@NguyenTrungHieu well, if you change the structure you will for sure have some issue :) as the solution it not 100% generic since it rely on some hacky trick, so maybe the code need to adjusted. And for the blink simply remive the transition if you don't need it;)Dryly
That's buggy when cell contents' text is long enough to wrap over multiple lines.Dhoti
Just for the record, it does not work with the gaps. So it is more "multiple cells" than a "row" notion.Ferdinand
C
7

Here's my solution, based on sibling combinators.
The main part is:

.datacell:hover ~ .datarow {
    background-color: rgba(255,255,0,0.5);
}
.datacell:hover ~ .datarow + .datacell ~ .datarow{
    background-color: transparent;
}

Snippet:

.datatable{
    display:grid;
    grid-gap: 0;
    grid-template-columns: auto 1fr auto;
    position: relative;
}

.datarow{
    grid-column: 1 / 4;
    z-index: 0;
}

.datacell{
    z-index: 1;
    padding: 8px;
    border-bottom: 1px solid #c0c0c0;
}

.datacell:hover ~ .datarow {
    background-color: rgba(255,255,0,0.5);
}
.datacell:hover ~ .datarow + .datacell ~ .datarow{
    background-color: transparent;
}

.row-1{ grid-row: 1;}
.row-2{ grid-row: 2;}
.row-3{ grid-row: 3;}

.col-1{ grid-column: 1;}
.col-2{ grid-column: 2;}
.col-3{ grid-column: 3;}
<div class="datatable">
   <div class="datacell col-1 row-1">Row 1 Column 1</div>
   <div class="datacell col-2 row-1">Row 1 Column 2</div>
   <div class="datacell col-3 row-1">Row 1 Column 3</div>       
   <div class="datarow row-1"></div>

   <div class="datacell col-1 row-2">Row 2 Column 1</div>
   <div class="datacell col-2 row-2">Row 2 Column 2</div>
   <div class="datacell col-3 row-2">Row 2 Column 3</div>       
   <div class="datarow row-2"></div>

   <div class="datacell col-1 row-3">Row 3 Column 1</div>
   <div class="datacell col-2 row-3">Row 3 Column 2</div>
   <div class="datacell col-3 row-3">Row 3 Column 3</div>       
   <div class="datarow row-3"></div>

</div>

The html must be structured so that a .datarow element closes the virtual grid row and spans all the preceding cells. Explicitation of the positioning inside the grid is needed in order to let cells and rows overlap.

Coma answered 6/11, 2018 at 10:5 Comment(0)
O
1

I ended up with a different solution. My table looked like this:

.table {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}
<div class="table">
  <span>head1</span>
  <span>head2</span>
  <span>head3</span>
  <span>row1 col1</span>
  <span>row1 col2</span>
  <span>row1 col3</span>
  <span>row2 col1</span>
  <span>row2 col2</span>
  <span>row2 col3</span>
</div>

Now you want the hover state per row. Adding an element wrapping each row breaks the parent grid as it would really be the children of those row wrappers you're trying to lay out in a grid. In addition, I preferred:

  • Not using a <table>. Which about doubled the number of lines needed and required more styling.
  • Not using nth-child puzzling.
  • One decent option I didn't try seems to be wrapping each row in a div, add a class .row and display: contents, then with .row:hover > span as a selector you could add opacity: 0.6. Seems decent! Some other answers go in this direction.

I ended up going for the following which I feel is simpler:

.row {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.row:hover {
  opacity: 0.6
}
<div class="row">
  <span>head1</span>
  <span>head2</span>
  <span>head3</span>
</div>
<div class="row">
  <span>row1 col1</span>
  <span>row1 col2</span>
  <span>row1 col3</span>
</div>
<div class="row">
  <span>row2 col1</span>
  <span>row2 col2</span>
  <span>row2 col3</span>
</div>
Offcolor answered 16/12, 2021 at 17:59 Comment(0)
O
1

Now that all big browsers support CSS Subgrids (and nesting), this is a much easier task

.myGrid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 10px;
  .myRow {
    grid-column: span 3;
    display: grid;
    grid-template-columns: subgrid;
    padding: 5px 0;
    &:hover {
      background-color: #f8f8f8;
    }
  }
}
<div class="myGrid">
  <span>head1</span>
  <span>head2</span>
  <span>head3</span>
  <div class="myRow">
    <span>row1 col1</span>
    <span>row1 col2</span>
    <span>row1 col3</span>
  </div>
  <div class="myRow">
    <span>row2 col1</span>
    <span>row2 col2</span>
    <span>row2 col3</span>
  </div>
  <div class="myRow">
    <span>row3 col1</span>
    <span>row3 col2</span>
    <span>row3 col3</span>
  </div>
  <div class="myRow">
    <span>row4 col1</span>
    <span>row4 col2</span>
    <span>row4 col3</span>
  </div>
  <div class="myRow">
    <span>row5 col1</span>
    <span>row5 col2</span>
    <span>row5 col3</span>
  </div>
  <div class="myRow">
    <span>row6 col1</span>
    <span>row6 col2</span>
    <span>row6 col3</span>
  </div>
</div>
Outspan answered 11/12, 2023 at 17:3 Comment(0)
F
0

Nowadays the solution is subgrid. It solves the problem with gaps in the main grid, the markup is somewhat similar to tr and table meaning you have nesting:

<div class="my-subgrid-row">
  <div class="cell-col1"></div>
  <div class="cell-col2"></div>
  ...
</div>

and CSS is quite straightforward without a lot of hacks.

Ferdinand answered 17/11, 2023 at 19:55 Comment(0)
L
-2

I have solution for your problem. The example below:

.grid .row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-bottom: 1px solid gray;
}
.grid .row div {
  padding: 10px;
  display: flex;
  align-items: center;
}
.grid .row:last-child {
  border: none;
}
.grid .row:hover {
  background-color: #cccccc;
  transition: .2s all;
}
<div class='grid'>
  <div class='row'>
    <div>Header</div>
    <div>Header</div>
    <div>Header</div>
    <div>Header</div>
  </div>
  <div class='row'>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
  </div>
  <div class='row'>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
  </div>
  <div class='row'>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
    <div>Content</div>
  </div>
</div>
Lordosis answered 21/8, 2018 at 7:26 Comment(2)
Please include an explanation of why this is the answer.Nonsectarian
this will not work for the set up where grid-template-column has auto, because some content may expand a column in one row more than in the otherMccahill

© 2022 - 2024 — McMap. All rights reserved.