Skip to content
Advertisement

show and hide associated sub menu when particular menu is clicked

I am designing a responsive mega navigation bar using reactjs. It’s working for desktop device but on mobile view, I could not able to show or hide sub menu when its associated parent menu is clicked. This is what I am trying to do

const menus = [
  { id: 1, name: "Home", link: "/" },
  {
    id: 3,
    name: "New",
    children: [{ id: 1, name: "New Arrival", link: "/new" }]
  },
  {
    id: 2,
    name: "Shop",
    children: [
      {
        id: 1,
        name: "Men's Fashion",
        children: [
          {
            id: 1,
            name: "Polo",
            link: "/polo"
          },
          { id: 2, name: "Tees", link: "/tees" }
        ]
      },
      {
        id: 2,
        name: "Women Fashion",
        children: [
          {
            id: 1,
            name: "Shorts",
            link: "/shorts"
          },
          {
            id: 2,
            name: "Tees",
            link: "/women/tees"
          }
        ]
      }
    ]
  }
];

const MegaMenu = () => {
  const [active, setActive] = React.useState(false);
  const [toggle, setToggle] = React.useState(false);
  const [activeMenuName, setActiveMenuName] = React.useState("");
  const handleMainMenuClick = () => {
    setActive(!active);
  };
  const toggleDrawer = () => {
    setToggle(!toggle);
  };
  const hideSubMenu = () => {
    setActive(!active);
  };
  const handleMenuName = (menuName) => {
    setActiveMenuName(menuName);
  };
  return (
    <>
      <header className="header">
        <div className="container">
          <div className="row v-center">
            <div className="header-item item-left">
              <div className="logo">
                <a href="#">Brand</a>
              </div>
            </div>
            {/* menu start here */}
            <div className="header-item item-center">
              <div
                className={`menu-overlay ${toggle ? "active" : ""}`}
                onClick={toggleDrawer}
              ></div>
              <nav className={`menu ${toggle ? "active" : ""}`}>
                <div className={`mobile-menu-head ${toggle ? "active" : ""}`}>
                  <div className="go-back" onClick={hideSubMenu}>
                    Back
                  </div>
                  <div className="current-menu-title">{activeMenuName}</div>
                  <div className="mobile-menu-close" onClick={toggleDrawer}>
                    &times;
                  </div>
                </div>
                <ul className="menu-main" onClick={handleMainMenuClick}>
                  {menus.map((menu) => {
                    return (
                      <li
                        key={menu.id}
                        className={
                          menu.children ? "menu-item-has-children" : ""
                        }
                        onClick={() => handleMenuName(menu.name)}
                      >
                        {menu.link ? (
                          <a href={menu.link}>
                            {menu.name} <i class="fa fa-angle-down"></i>
                          </a>
                        ) : (
                          <a href="#">{menu.name}</a>
                        )}
                        {menu.children && (
                          <div
                            className={`sub-menu mega-menu mega-menu-column-4 ${
                              active ? "active" : ""
                            }`}
                          >
                            {menu.children.map((child) => {
                              return (
                                <div
                                  className="list-item text-center"
                                  key={child.id}
                                >
                                  <a href="#">
                                    <img src="img/p1.jpg" alt="new Product" />
                                    <h4 className="title">{child.name}</h4>
                                  </a>
                                </div>
                              );
                            })}
                          </div>
                        )}
                      </li>
                    );
                  })}
                </ul>
              </nav>
            </div>
            <div className="header-item item-right">
              <a href="#">
                <i className="fas fa-search"></i>
              </a>
              <a href="#">
                <i className="far fa-heart"></i>
              </a>
              <a href="#">
                <i className="fas fa-shopping-cart"></i>
              </a>
              {/* mobile menu trigger */}
              <div className="mobile-menu-trigger" onClick={toggleDrawer}>
                <span></span>
              </div>
            </div>
          </div>
        </div>
      </header>
    </>
  );
};

export default MegaMenu;

I have a code on sandbox as well and here it is

https://codesandbox.io/s/weathered-resonance-9n2hr?file=/src/mega-menu/index.js:50-4734

Advertisement

Answer

You should set active only to the activeMenuName that is clicked. Also when click go back you should reset activeMenuName to the default state. Also, I check if toggle is enabled so only onClick events are enabled and vice versa.

Check my code it’s tested.

const MegaMenu = () => {
  const [active, setActive] = React.useState(false);
  const [toggle, setToggle] = React.useState(false);
  const [activeMenuName, setActiveMenuName] = React.useState("");
  const handleMainMenuClick = () => {
    setActive(!active);
  };
  const toggleDrawer = () => {
    setActiveMenuName("");
    setToggle(!toggle);
  };
  const hideSubMenu = () => {
    setActiveMenuName("");
  };
  const handleMenuName = (menuName) => {
      setActiveMenuName(menuName);
  };
  
  const renderLiComp = (menu) =>{
      if(toggle){
            return <li
            key={menu.id}
            className={ menu.children ? "menu-item-has-children" : "" }
            onClick={() =>handleMenuName(menu.name)}
            >
              {menu.link 
                ? (<a href={menu.link}>{menu.name} <i className="fa fa-angle-down"></i></a>) 
                : (<a href="#">{menu.name}</a>)}
              {activeMenuName && getSelectedChildren()}
            </li>;
      }else{
        return <li
          key={menu.id}
          className={ menu.children ? "menu-item-has-children" : "" }
          onMouseEnter={()=>handleMenuName(menu.name)}
          >
            {menu.link 
              ? (<a href={menu.link}>{menu.name} <i className="fa fa-angle-down"></i></a>) 
              : (<a href="#">{menu.name}</a>)}
            {activeMenuName && getSelectedChildren()}
          </li>;
      }

  }

  const renderListElements = () =>{

    return menus.map(renderLiComp);
  }
  
  const renderChildren = (childs) =>{
    const children = childs.map(x =>
                <div className="list-item text-center" key={x.id}>
                  <a href="#"><img src="img/p1.jpg" alt="new Product" />
                     <h4 className="title">{x.name}</h4>
                  </a>
                </div>
              );
   return children;
  }

  const getSelectedChildren = () =>{
      return menus.map(x => {
        let selected;
        if(x.name === activeMenuName){
           if(x.children){
             selected = <div key={x.id} className='sub-menu mega-menu mega-menu-column-4 active'>{renderChildren(x.children)}</div>;
           }else{
             selected = <div key={x.id} className='sub-menu mega-menu mega-menu-column-4 active'></div>; 
           }
        }
        return selected; 
      });
  }

      return (
        <>
          <header className="header">
            <div className="container">
              <div className="row v-center">
                <div className="header-item item-left">
                  <div className="logo">
                    <a href="#">Brand</a>
                  </div>
                </div>
                {/* menu start here */}
                <div className="header-item item-center">
                  <div
                    className={`menu-overlay ${toggle ? "active" : ""}`}
                    onClick={toggleDrawer}
                  ></div>
                  <nav className={`menu ${toggle ? "active" : ""}`}>
                    <div className={`mobile-menu-head ${toggle ? "active" : ""}`}>
                      <div className="go-back" onClick={hideSubMenu}>
                        Back
                      </div>
                      <div className="current-menu-title">{activeMenuName}</div>
                      <div className="mobile-menu-close" onClick={toggleDrawer}>
                        &times;
                      </div>
                    </div>
                    <ul className="menu-main" onClick={handleMainMenuClick}>
                      {renderListElements()}
                    </ul>
                  </nav>
                </div>

The sandbox Link https://codesandbox.io/s/nifty-cloud-kuryf?file=/src/mega-menu/index.js

Advertisement