Nguyen Trung Hieu
Nguyen Trung Hieu

Reputation: 553

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

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>

Upvotes: 53

Views: 30282

Answers (7)

Bach The Vinh
Bach The Vinh

Reputation: 11

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>

Upvotes: -2

Steve -Cutter- Blades
Steve -Cutter- Blades

Reputation: 5432

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>

Upvotes: 1

greenoldman
greenoldman

Reputation: 21072

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.

Upvotes: -1

alextes
alextes

Reputation: 2249

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>

Upvotes: 1

Peter Rodes
Peter Rodes

Reputation: 761

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>

Upvotes: 76

Cristian Merighi
Cristian Merighi

Reputation: 1631

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.

Upvotes: 7

Temani Afif
Temani Afif

Reputation: 273022

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>

Upvotes: 19

Related Questions