How do I use CSS to select a "cousin" element

108 Views Asked by At

I have the following (simplified) HTML

<ul class="sidenav">
    <li class="tree-node">
        <a href="url">
            <span class="submenu-toggle-container">Text</span>
        </a>
        <ul class="accordion-menu">
            <li class="HideMenu">.......</li>
        </ul>
    </li>
    <li class="tree-node">...</li>
</ul>

I am trying to use CSS to hide the .submenu-toggle-container span when the sibling is ul li.HideMenu.

I have been able to make this work using jQuery code $('ul.sidenav li.HideMenu').parent('ul').siblings('a').children('.submenu-toggle-container').hide(); but this is not optimal for my project and I would much rather use CSS to make this work.

My attempts include

ul.sidenav li.HideMenu ~ ul a .submenu-toggle-container {
  display: none;
}

but this does not work.

Any help is appreciated.

3

There are 3 best solutions below

1
On BEST ANSWER

Try :has

a:has(+ ul.accordion-menu li.HideMenu) .submenu-toggle-container {
  display: none;
}
<ul class="sidenav">
  <li class="tree-node">
    <a href="url">
      <span class="submenu-toggle-container">Text</span>
    </a>
    <ul class="accordion-menu">
      <li class="HideMenu">.......</li>
    </ul>
  </li>
  <li class="tree-node">...</li>
</ul>

Does not supported by firefox if layout.css.has-selector.enabled is fasle.

enter image description here

0
On

Shuo beat me to it but you could pull this off with the :has selector:

/*
  select .submenu-toggle-container children of
  .tree-node that has a li.HideMenu child
*/
.tree-node:has(ul li.HideMenu) .submenu-toggle-container {
  display: none;
}
<ul class="sidenav">
    <li class="tree-node">
        <a href="url">
            <span class="submenu-toggle-container">Text</span>
        </a>
        <ul class="accordion-menu">
            <li class="HideMenu">.......</li>
        </ul>
    </li>
    <li class="tree-node">...</li>
</ul>

1
On

The first answer here by Shuo is the same as mine, but I'll leave this here in case anyone wants to read more into how this solution works.

This can be accomplished through the use of the recent :has() psuedo-class.

Here's a quick bit of CSS to show how it can work:

li.tree-node a:has(+ ul.accordion-menu li.HideMenu) .submenu-toggle-container {
  background: red;
}
<ul class="sidenav">
    <li class="tree-node">
        <a href="url">
            <span class="submenu-toggle-container">Hide Me</span>
        </a>
        <ul class="accordion-menu">
            <li class="HideMenu">Hide Menu</li>
        </ul>
    </li>
    <li class="tree-node">...</li>
</ul>

I used background: red; because it's easier to show something than to not show something, so you'd just need to replace that with display: none;

So, how does it work?

To start, we cannot select a parent in CSS, so we need to work through siblings. This means that the route to the .HideMenu element has to be from the a to its sibling ul and then the li. This is why we write the a:has(+ ul.accordion-menu li.HideMenu) selector.

With the above, we've established the relationship through the sibling to the desired element to toggle your submenu. All that's needed is to write the next selector to target that specific decendant.

Great? Right! Well, beware -- there is a catch: this solution is not supported in Firefox.

To be able to do this same thing in Firefox, you will need to write JS/JQuery code to add support for this feature yourself.