skip to Main Content

In the following code I have a nested menu. On hover I wanted to add selected class name to the Nav.Item and only remove it when I hover over another Nav.Item. I could achive that with onMouseOver. Unfortunately, it doesn’t add selected class to any of the sub-level Nav.Item.

So the criterias would be:
for hover on any nav.item selected class should be added

  • don’t remove selected class if mouse leaves the item
  • keep selected class on the Nav.Item when a child menu appears
  • remove selected class only, if hover over another Nav.Item but on the same level
"use client"; 
 
import Nav from 'react-bootstrap/Nav'; 
import Link from 'next/link'; 
 
import { getMenu } from '@/lib/APIs/menu'; 
import { use, useEffect, useRef, useState } from 'react'; 
import { usePathname } from 'next/navigation'; 
import { Button, NavDropdown } from 'react-bootstrap'; 
 
const dataPromise = getMenu(); 
 
const GlobalNav = () => { 
    const MENU = use(dataPromise); 
    const [selectedItemId, setSelectedItemId] = useState<string | null>(null); 
 
    function handleItemHover(itemId: string) { 
        setSelectedItemId(itemId); 
    } 
 
    function renderMenuItems(menuItems: any[]) { 
        return menuItems.map((item: any) => ( 
            <Nav.Item 
                as='li' 
                key={item.id}    
                onMouseOver={() => handleItemHover(item.id)} 
                className={selectedItemId === item.id ? 'selected' : ''} 
            > 
                <Nav.Link 
                    as={Link} 
                    href={item.url} 
                > 
                    <span> 
                        {item.title} 
                    </span> 
                </Nav.Link> 
                {item.children && item.children.length > 0 && ( 
                    <Nav as='ul'> 
                        {renderMenuItems(item.children)} 
                    </Nav> 
                )} 
            </Nav.Item> 
        )); 
    } 
 
    return ( 
        <Nav as='ul'> 
            {renderMenuItems(MENU)} 
        </Nav> 
    ); 
} 
 
export default GlobalNav; 

I hope someone can help with it.

3

Answers


  1. Maybe the problem is with the Python version or the problem with the pygame library or the problem is with your computer

    Login or Signup to reply.
  2. Try this:

    function hasSelectedChild(menuItem) {
       return menuItem.children && menuItem.children.length > 0 && menuItem.children.find(item => item.id === selectedItemId || hasSelectedChild(item)) 
    }
    
    function renderMenuItems(menuItems: any[]) { 
            return menuItems.map((item: any) => ( 
                <Nav.Item 
                    as='li' 
                    key={item.id}    
                    onMouseOver={(e) => {
                      e.stopPropagation();
                      handleItemHover(item.id)
                    }} 
                    className={(hasSelectedChild(item) || selectedItemId === item.id) ? 'selected' : ''}
                > 
                    <Nav.Link 
                        as={Link} 
                        href={item.url} 
                    > 
                        <span> 
                            {item.title} 
                        </span> 
                    </Nav.Link> 
                    {item.children && item.children.length > 0 && ( 
                        <Nav as='ul'> 
                            {renderMenuItems(item.children)} 
                        </Nav> 
                    )} 
                </Nav.Item> 
            )); 
        }
    
    Login or Signup to reply.
  3. To achieve the desired behavior of adding the "selected" class to the Nav.Item elements on hover and keeping it when a child menu appears, you can modify the code as below:

    import Nav from 'react-bootstrap/Nav';
    import Link from 'next/link';
    import { getMenu } from '@/lib/APIs/menu';
    import { usePathname } from 'next/navigation';
    import { Button, NavDropdown } from 'react-bootstrap';
    
    const dataPromise = getMenu();
    
    const GlobalNav = () => {
      const MENU = use(dataPromise);
      const [selectedItemId, setSelectedItemId] = useState<string | null>(null);
    
      function handleItemHover(itemId: string) {
        setSelectedItemId(itemId);
      }
    
      function handleItemMouseLeave() {
        setSelectedItemId(null);
      }
    
      function renderMenuItems(menuItems: any[]) {
        return menuItems.map((item: any) => (
          <Nav.Item
            as='li'
            key={item.id}
            onMouseOver={() => handleItemHover(item.id)}
            onMouseLeave={handleItemMouseLeave}
            className={selectedItemId === item.id ? 'selected' : ''}
          >
            <Nav.Link as={Link} href={item.url}>
              <span>{item.title}</span>
            </Nav.Link>
            {item.children && item.children.length > 0 && (
              <Nav as='ul' className={selectedItemId === item.id ? 'selected' : ''}>
                {renderMenuItems(item.children)}
              </Nav>
            )}
          </Nav.Item>
        ));
      }
    
      return (
        <Nav as='ul'>
          {renderMenuItems(MENU)}
        </Nav>
      );
    }
    
    export default GlobalNav;
    

    Adding the className prop to the nested Nav component to add the "selected" class when the parent Nav.Item is selected. And adding the onMouseLeave={handleItemMouseLeave} event handler to the Nav.Item to trigger the reset of selectedItemId

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search