Notbad
Notbad

Reputation: 6296

How to show a child or another depending on parent hover state (CSS Only)

I'm building a Menu component in react that shows a list of options. Each option has a text on the left and the '>' icon on the right. But, when I hover on the parent I want the '>' symbol to change to '>>'. How can I do this? I have added the current state of the source. I just need to hide BsChevronRight on parent hover and show BsChevronDoubleRight and viceversa.

function Menu({data}:{data:IMenuItemData[]}) {
    return (
        <div className="w-2/12 flex flex-col absolute top-2/4">
            {data.map(el => {
                return <a href="#" className="flex row justify-between items-center group">
                        {el.text}
                        <BsChevronRight></BsChevronRight>
                        <BsChevronDoubleRight></BsChevronDoubleRight>
                        </a>
                    
            })}
        </div>    
        
    )
}

I'm looking for a CSS only solution.

Upvotes: 4

Views: 5265

Answers (4)

jacobedawson
jacobedawson

Reputation: 3202

There's a quick way to do this in Tailwind:

1.) Add a "group" class to the parent e.g.

<div class="group i-am-parent">

2.) Add the hover effect on the child, prefixing with group e.g.

<div class="i-am-child group-hover:scale-110">

More info in the tailwind docs: https://tailwindcss.com/docs/hover-focus-and-other-states

Upvotes: 3

Ihar Aliakseyenka
Ihar Aliakseyenka

Reputation: 14183

Add hidden group-hover:block classes for your second chevron (that should appear) or any fadeIn effect like: invisible group-hover:visible or opacity-0 group-hover:opacity-100. Your parent a element should have group class to work.

Note: if you're using TailwindCSS without jit mode enabled you need to extend some properties to support group-hover variant

// tailwind.config.js
module.exports = {
  variants: {
      extend: {
        display: ['group-hover'],
        visibility: ['group-hover'],
      }
   }
}

DEMO: https://play.tailwindcss.com/XifyCwLkFV

Upvotes: 3

Notbad
Notbad

Reputation: 6296

Well, it seems that I go a solution. It feels a bit hacky to me but It works. The trick is to have a parent and 2 children that flip the display property between none and block.

For me it does the job. If anyone has a better solution I'm all ears.

HTML:

<html>
  <body>
    <div class="parent">
      Hello
      <div class="child1"></div>
      <div class="child2"></div>
    </div>
  </body>
</html>

CSS:

.parent {
   width:300px;
   height:300px;
   background-color:yellow;
   display:flex;
}

.parent:hover > .child1 {
  display:block;
}

.parent:hover > .child2 {
  display:none;
}

.child1 {
  display:none;
  width: 100px;
  height:100px;
  background-color: green;
}

.child2 {
  display:block;
  width: 100px;
  height:100px;
  background
}

https://jsfiddle.net/pyfL7r03/42/

Upvotes: 1

Ahmed Ghonem
Ahmed Ghonem

Reputation: 22

Try creating a state isHovered and toggle using onMouseEnter and onMouseLeave

function Menu({data}:{data:IMenuItemData[]}) {
    const [isHovered , setIsHovered] = useState(false);
    const handleMouseEnter = ()=>{
          setIsHovered(true)
    }
    const handleMouseLeave = ()=>{
          setIsHovered(false)
    }
    return (
        <div className="w-2/12 flex flex-col absolute top-2/4">
            {data.map(el => {
                return <a onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave} href="#" className="flex row justify-between items-center group">
                        {el.text}
                        {!isHovered ? <BsChevronRight/> : 
                        <BsChevronDoubleRight/>}
                        </a>
                    
            })}
        </div>    
        
    )
}

Upvotes: 0

Related Questions