49

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>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Nguyen Trung Hieu
  • 513
  • 1
  • 4
  • 9

6 Answers6

70

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>
Prinzhorn
  • 22,120
  • 7
  • 61
  • 65
Peter Rodes
  • 701
  • 1
  • 5
  • 4
  • 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. – JohnF Aug 10 '19 at 08:59
  • 1
    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. – Prinzhorn Feb 01 '20 at 12:15
  • 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. – richardaum Mar 17 '21 at 19:47
  • I really should try to keep up with the new aditions to css, display: contents is a killer feature. Thank you a lot, you saved me! – Vinícius Negrão Apr 23 '21 at 18:45
  • `display: contents` is unsupported in IE11. FYI – Nathan Goings May 12 '21 at 23:35
  • 1
    @NathanGoings IE11 is unsupported by Microsoft FYI – Maciej Kwas May 28 '21 at 12:27
  • @MaciejKwas, I'm seeing June 2022 as end-of-support. Considering that 365 still supports it, I'm not sure where you got your info. – Nathan Goings May 30 '21 at 04:42
  • 1
    @NathanGoings You mean end-of-technical-support. Official Microsoft Edge docs: "[...]Site developers should focus their testing on Microsoft Edge for new and existing experiences. Internet Explorer 11 will be included for some legacy scenarios [...]". Last feature update for IE11 was in 2015, now it's 2021 and IE is gone for good. 365 support doesn't mean anything, they could support even IE6 if they want to, yet- end of features development is the end of support.: https://blogs.windows.com/windowsexperience/2021/05/19/the-future-of-internet-explorer-on-windows-10-is-in-microsoft-edge/ – Maciej Kwas May 30 '21 at 15:58
  • @MaciejKwas, feature support and security support are two different things. There are plenty of enterprise web-developers who are stuck with supporting IE11 because it's still usable by their company. Thankfully, in my enterprise, IE11 will be dropped when security support ends. – Nathan Goings Jun 01 '21 at 16:48
  • 2
    @NathanGoings nope, they are not stuck. When I was forced to support IE11 in my company- I left it, and it was 2017 back then. It is company problem that it can not port to new technologies easily, not the developers problem. I can't imagine learning new stuff, discovering new technologies and on the other hand still supporting IE11. Cheers :) – Maciej Kwas Jun 01 '21 at 20:15
18

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>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
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.

Cristian Merighi
  • 1,631
  • 2
  • 13
  • 9
  • This is simplest solution I ever saw. I learned `+` and `~` sibling combinator from this answer. Thank you. – doctorgu Feb 09 '22 at 03:14
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>
alextes
  • 1,817
  • 2
  • 15
  • 22
-3

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>
-5

I had this problem. First, I'm curious to know why if you are using display grid, how come you are not using the grid-item class? is that not necessary? I think it is. Anyway, the answer is, you need to give all your div elements the grid-item class and set the position to Relative.

.grid-item {
  position: relative;
}
ROOT
  • 11,363
  • 5
  • 30
  • 45
James II
  • 1
  • 1
  • Sorry. I think this absolutely is not an answer at all. I don't see how the proposed attribute would help the author in any way I could think of. Your mentioned class `.grid-item` was not even specified in the original example, and if you're referring to the grid cells in general, which the author classified with `.row`, I still don't see how your proposal would achieve anything here. – nether_cat Feb 20 '20 at 19:13