0

I have the following simple header component with navigation items, which when hovered on should add the following two css rules to the mega-menu div element so that it appears:

visibility: visible;
opacity: 1;

.nav-menu {
  min-height: 112px;
  padding-left: 5.6rem;
  padding-right: 5.6rem;
  background-color: green;
  display: flex;
  gap: 24px;
  align-items: center;
  justify-content: center;
}

.nav-menu li {
  list-style: none;
}

.nav-menu li a {
  color: white;
  text-decoration: none;
}

.nav-menu li a:hover{
  text-decoration: underline;
}

.mega-menu {
  visibility: hidden;
  opacity: 0;
  position: absolute;
  background-color: lightblue;
  width: 100%;
  min-height: 250px;
  top: 128px;
  text-align: center;
}

.nav-menu li a:hover .mega-menu {
  visibility: visible; 
  opacity: 1;
}
<div class="container">
  <ul class="nav-menu">
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
  </ul>

  <div class="mega-menu">
    This mega menu should only be visible when one of the list items in the nav
    menu is hovered on (and then once open will close when no longer hovering on
    the mega menu)
  </div>
</div>

However, as observed through my snippet, I'm struggling to update the mega-menu class when the a in the nav-menu is hovered on. Ideally the mega-menu should be visible when a list item is hovered on, or the user is hovering on the mega-menu (otherwise it should not be visible).

Any pointers as to where I'm going wrong would be really appreciated.

cts
  • 908
  • 1
  • 9
  • 30
  • Looks like you have not understood the meaning of the word "sibling". – CBroe Apr 19 '23 at 11:13
  • I omitted the parent container from the snippet to reduce extra code from the snippet but assumed the context was clear, will update. thanks! – cts Apr 19 '23 at 11:15
  • 1
    Also, you did not even use any sibling combinator to begin with - `a:hover .mega-menu` would affect a `.mega-menu` element that was a _descendant_ of the hovered link. – CBroe Apr 19 '23 at 11:17
  • That parent container still doesn't make the links and `.mega-menu` _siblings_. – CBroe Apr 19 '23 at 11:18

2 Answers2

3

This can be done without using :has() (which still has no support in FIrefox, unless the user explicitly enables it), and without JavaScript.

First of all, the .mega-menu is a sibling of the UL - so we use .nav-menu:hover + .mega-menu as our selector, to make it visible, when the UL gets hovered.

But you probably don't want it to show already when the list itself gets hovered, but only when one of the actual navigation items gets hovered?

That can be achieved here by putting pointer-events: none on the UL, and "reversing" that again with pointer-events: all on the LI.

.nav-menu {
  min-height: 112px;
  padding-left: 5.6rem;
  padding-right: 5.6rem;
  background-color: green;
  display: flex;
  gap: 24px;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.nav-menu li {
  list-style: none;
  pointer-events: all;
}

.nav-menu li a {
  color: white;
  text-decoration: none;
}

.nav-menu li a:hover{
  text-decoration: underline;
}

.mega-menu {
  visibility: hidden;
  opacity: 0;
  position: absolute;
  background-color: lightblue;
  width: 100%;
  min-height: 250px;
  top: 128px;
  text-align: center;
}

.nav-menu:hover + .mega-menu {
  visibility: visible; 
  opacity: 1;
}
<div class="container">
  <ul class="nav-menu">
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
    <li>
      <a href="#">Navigation item</a>
    </li>
  </ul>

  <div class="mega-menu">
    This mega menu should only be visible when one of the list items in the nav
    menu is hovered on (and then once open will close when no longer hovering on
    the mega menu)
  </div>
</div>
CBroe
  • 91,630
  • 14
  • 92
  • 150
  • 2
    Btw., you can avoid using that "magic number" `top: 128px;` (which might be problematic, if the UL was not always _exactly_ as high as you expected, for example due to differences in actual fonts being used to render this on the client), if you make the container the reference point by adding `position: relative` to that, and then using `top: 100%`. – CBroe Apr 19 '23 at 11:30
  • Thanks, that is really comprehensive and helpful. The only issue that remains is that the user can't interact with the mega menu. Due to the limited height of the hoverable area of the list items in the `nav-menu` - the mega menu doesn't stay visible whilst the user goes from hovering on the links, to hovering on the mega menu itself. – cts Apr 19 '23 at 11:56
  • Yes, that's right. But you would have had that same issue already, if there actually was a way to show this element based on just `a:hover` here. You will simply have to make your menu items high enough then, that they reach the lower corner of the UL. "Gaps" for the mouse cursor to fall into the between the triggering element and the target, are seldom a good idea. – CBroe Apr 19 '23 at 12:01
  • (Or you could use a pseudo element, that only "extends" the hoverable link area to the bottom of the UL, _when_ the link gets hovered.) – CBroe Apr 19 '23 at 12:02
0

One way to do it using CSS only is by using the has selector on the common container, for example:

.container:has(a:hover) .mega-menu{
  visibility: visible;
  opacity: 1;
}

The problem is that right now the has selector is not available on all browsers, Firefox does not implement it yet for example.

mck89
  • 18,918
  • 16
  • 89
  • 106