Show the menu when hovering in reactjs

Currently, in my react project, I am implementing dropdown menus using the reactstrap css framework.

Example.Js

      <Dropdown
        className="d-inline-block"
        onMouseOver={this.onMouseEnter}
        onMouseLeave={this.onMouseLeave}
        isOpen={this.state.dropdownOpen}
        toggle={this.toggle}
      >
        <DropdownToggle caret>Dropdown1</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 1</DropdownItem>
          <DropdownItem>Submenu 1.1</DropdownItem>
        </DropdownMenu>
        &nbsp;&nbsp;&nbsp;
        <DropdownToggle caret>Dropdown2</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 2</DropdownItem>
          <DropdownItem>Submenu 2.1</DropdownItem>
          <DropdownItem>Submenu 2.2</DropdownItem>
        </DropdownMenu>
        &nbsp;&nbsp;&nbsp;
        <br /><br />
        <DropdownToggle caret>Dropdown3</DropdownToggle>
        <DropdownMenu>
          <DropdownItem header>Submenu 3</DropdownItem>
          <DropdownItem>Submenu 3.1</DropdownItem>
          <DropdownItem>Submenu 3.2</DropdownItem>
          <DropdownItem>Submenu 3.3</DropdownItem>
        </DropdownMenu>
      </Dropdown>

In the above code, I have used the setState method to manage the state of dropdownOpen based on the onMouseOver and onMouseLeave events.

However, the main problem I am facing is that when I hover over a single dropdown menu, all dropdowns open at the same time.

To see a Working Demo, click here.

I am looking for assistance in modifying the hover dropdown functionality to only display the menu of the dropdown being hovered over and not all menus simultaneously.

Note: The dropdown menus in my actual application will be dynamically generated, and hence, I cannot use hardcoded states like dropDown1, dropDown2, dropDown3, etc.

Since the number of dropdowns may vary, I need a solution that can handle the dynamic nature of the menus.

Answer №1

Essentially, the approach is to have each dropdown menu within its own encapsulated Dropdown composite component, complete with its own set of state and event handlers. A modified version of your demo has been created to illustrate this concept.

<div>
    <Dropdown
      className="d-inline-block"
      onMouseOver={this.onMouseEnter}
      onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen1}
      toggle={this.toggle1}
    >
      <DropdownToggle caret>Dropdown1</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 1</DropdownItem>
        <DropdownItem>Submenu 1.1</DropdownItem>
      </DropdownMenu>
      &nbsp;&nbsp;&nbsp;
    </Dropdown>
    <Dropdown
      className="d-inline-block"
      // onMouseOver={this.onMouseEnter}
      // onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen2}
      toggle={this.toggle2}
    >

      <DropdownToggle caret>Dropdown2</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 2</DropdownItem>
        <DropdownItem>Submenu 2.1</DropdownItem>
        <DropdownItem>Submenu 2.2</DropdownItem>
      </DropdownMenu>
      &nbsp;&nbsp;&nbsp;
      <br /><br />

    </Dropdown>
    <Dropdown
      className="d-inline-block"
      // onMouseOver={this.onMouseEnter}
      // onMouseLeave={this.onMouseLeave}
      isOpen={this.state.dropdownOpen3}
      toggle={this.toggle3}
    >

      <DropdownToggle caret>Dropdown3</DropdownToggle>
      <DropdownMenu>
        <DropdownItem header>Submenu 3</DropdownItem>
        <DropdownItem>Submenu 3.1</DropdownItem>
        <DropdownItem>Submenu 3.2</DropdownItem>
        <DropdownItem>Submenu 3.3</DropdownItem>
      </DropdownMenu>
    </Dropdown>
  </div>

https://stackblitz.com/edit/reactstrap-v6-2dnzex?file=Example.js

Answer №2

Here is the code snippet to display the submenu of a menu or dropdown. If you have any questions or need clarification, feel free to ask.

import React from 'react';
import { Box, jsx } from 'theme-ui';
import { Link } from 'gatsby';
import { H1 } from '../../components/ThemeHeader';
import { Image } from '../../components';

const CardWithCTALinks = (props) => {
  const { cardWithCTALinksImage, ctaLinks, heading, bgColor } = props;
  const [onCTAListHover, setOnCTAListHover] = React.useState({ status: '', indexAt: -1 });

  const updateCTAListHover = (newOnCTAListHover, idx) => {
    if (newOnCTAListHover !== onCTAListHover) setOnCTAListHover({ state: newOnCTAListHover, indexAt: idx });
  };
  const renderImageSection = (src, alt) => {
    return <Image src={src} alt={alt} />;
  };
  const renderSubLinks = (subLinks, idx) => {
    return (
      <Box>
        {idx === onCTAListHover.indexAt &&
          subLinks.map((link) => (
            <Link key={Math.random().toString(36).substring(7)} to="/">
              {link.text}
            </Link>
          ))}
      </Box>
    );
  };
  const renderLinksSection = (linksList, headingText) => {
    return (
      <Box>
        {headingText && <H1>{headingText}</H1>}
        {linksList && (
          <Box>
            {linksList.map((link, index) => (
              <h1 onMouseEnter={() => updateCTAListHover(true, index)} onMouseLeave={() => updateCTAListHover(false, index)}>
                {link.node?.title}
                {link.node?.navItems.length > 0 && <>{onCTAListHover && renderSubLinks(link.node?.navItems, index)}</>}
              </h1>
            ))}
          </Box>
        )}
      </Box>
    );
  };

  return (
    <Box style={{ backgroundColor: bgColor }}>
      {cardWithCTALinksImage && <Box>{renderImageSection(cardWithCTALinksImage?.asset._ref, 'alt')}</Box>}
      {ctaLinks && heading && <Box>{renderLinksSection(ctaLinks.edges, heading)}</Box>}
    </Box>
  );
};

export default CardWithCTALinks;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Answer №3

 import React from "react";
import {
  Dropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem
} from "reactstrap";

export default class CustomDropdown extends React.Component {
  constructor(props) {
    super(props);

    this.toggleMenu = this.toggleMenu.bind(this);
    this.handleMouseEnter = this.handleMouseEnter.bind(this);
    this.handleMouseLeave = this.handleMouseLeave.bind(this);
    this.state = {
      menuOpen1: false,
      menuOpen2: false,
      menuOpen3: false
    };
  }

  toggleMenu(id) {
   this.setState({[id]:!this.state[`${id}`]})
  }

  handleMouseEnter(id) {
    this.setState({ [id]: true });
  }

  handleMouseLeave(id) {
    this.setState({ [id]: false });

  }

  render() {
    return (
      <div>
        <Dropdown
          className="d-inline-block"
          onMouseOver={()=>this.handleMouseEnter("menuOpen1")}
          onMouseLeave={()=>this.handleMouseLeave("menuOpen1")}
          isOpen={this.state.menuOpen1}
          toggle={()=>this.toggleMenu("menuOpen1")}
        >
          <DropdownToggle caret>Dropdown1</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 1</DropdownItem>
            <DropdownItem>Submenu 1.1</DropdownItem>
          </DropdownMenu>
        </Dropdown>
        <Dropdown
          className="d-inline-block"
           onMouseOver={()=>this.handleMouseEnter("menuOpen2")}
          onMouseLeave={()=>this.handleMouseLeave("menuOpen2")}
          isOpen={this.state.menuOpen2}
          toggle={()=>this.toggleMenu("menuOpen2")}
        >
          &nbsp;&nbsp;&nbsp;
          <DropdownToggle caret>Dropdown2</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 2</DropdownItem>
            <DropdownItem>Submenu 2.1</DropdownItem>
            <DropdownItem>Submenu 2.2</DropdownItem>
          </DropdownMenu>
        </Dropdown>
        <Dropdown
          className="d-inline-block"
        onMouseOver={()=>this.handleMouseEnter("menuOpen3")}
          onMouseLeave={()=>this.handleMouseLeave("menuOpen3")}
          isOpen={this.state.menuOpen3}
          toggle={()=>this.toggleMenu("menuOpen3")}
        >
          &nbsp;&nbsp;&nbsp;
          <br />
          <br />
          <DropdownToggle caret>Dropdown3</DropdownToggle>
          <DropdownMenu>
            <DropdownItem header>Submenu 3</DropdownItem>
            <DropdownItem>Submenu 3.1</DropdownItem>
            <DropdownItem>Submenu 3.2</DropdownItem>
            <DropdownItem>Submenu 3.3</DropdownItem>
          </DropdownMenu>
        </Dropdown> 
      </div>
    );
  }
}

Give it a try! Remember to provide different arguments for each dropdown so the method can distinguish between which one to open or close.

Answer №4

Hello @TestUser and Drew Reese, I've optimized some code by implementing arrow functions. Check it out here: https://stackblitz.com/edit/reactstrap-dropdown?file=Example.js. If you require multiple dropdowns, you can create a single dropdown component and use props to generate multiple instances. Utilizing map for iteration, you can easily incorporate this functionality in various parts of your app. This showcases the flexibility and beauty of React.

Answer №5

Essentially, you were using the same state variable for all three dropdowns.

To resolve this issue, you should maintain three distinct state variables. To achieve this dynamically, you can follow the steps outlined below.

For a more reusable approach, consider creating the Dropdown as a separate component.

If needed, additional logic can be added. This is a straightforward way to address the problem.

App.js

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import { Container } from "reactstrap";
import CustomDropDown from "./CustomDropdown";
import "bootstrap/dist/css/bootstrap.min.css";

import "./styles.css";

const dropdownConfig = [
  {
    customKey: 1,
    options: [
      { title: "Submenu 1", header: true },
      { title: "Submenu 1.1", header: false }
    ],
    name: "dropdownOpen1"
  },
  {
    customKey: 2,
    options: [
      { title: "Submenu 2", header: true },
      { title: "Submenu 2.1", header: false }
    ],
    name: "dropdownOpen2"
  },
  {
    customKey: 3,
    options: [
      { title: "Submenu 3", header: true },
      { title: "Submenu 3.1", header: false }
    ],
    name: "dropdownOpen3"
  }
];

function App() {
  const [keysForDropdown, setKeysForDropdown] = useState({});

  useEffect(() => {
    const keys = dropdownConfig.map(dropdown => dropdown.name);
    const object = keys.reduce((acc, curr) => {
      acc[curr] = false;
      return acc;
    }, {});
    setKeysForDropdown({ ...object });
  }, []);

  const _handleToggle = e => {
    setKeysForDropdown({
      ...keysForDropdown,
      [e.target.name]: !keysForDropdown[e.target.name]
    });
  };

  const _handleMouseEnter = e => {
    setKeysForDropdown({
      ...keysForDropdown,
      [e.target.name]: !keysForDropdown[e.target.name]
    });
  };

  const _handleMouseLeave = e => {
    setKeysForDropdown({
      ...keysForDropdown,
      [e.target.name]: !keysForDropdown[e.target.name]
    });
  };

  return (
    <div className="App">
      <Container>
        {keysForDropdown &&
          dropdownConfig.map(dropdown => (
            <CustomDropDown
              {...dropdown}
              key={dropdown.customKey}
              stateKeys={keysForDropdown}
              handleToggle={_handleToggle}
              handleMouseEnter={_handleMouseEnter}
              handleMouseLeave={_handleMouseLeave}
            />
          ))}
      </Container>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

CustomDropdown.js

import React from "react";
import {
  Dropdown,
  DropdownToggle,
  DropdownMenu,
  DropdownItem
} from "reactstrap";

const CustomDropDown = props => {
  const {
    handleMouseEnter,
    handleMouseLeave,
    handleToggle,
    options,
    name,
    stateKeys
  } = props;
  return (
    <div className="dropdown-container">
      <Dropdown
        className="d-inline-block"
        onMouseOver={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
        isOpen={stateKeys[name]}
        toggle={handleToggle}
      >
        <DropdownToggle name={name} caret>
          Dropdown1
        </DropdownToggle>
        <DropdownMenu>
          {options.length &&
            options.map(({ header, title }) => (
              <DropdownItem header={header}>{title}</DropdownItem>
            ))}
        </DropdownMenu>
      </Dropdown>
    </div>
  );
};

export default CustomDropDown;

Working codesandbox

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Finding a problem in node.js

I have encountered a problem while creating a server game using node.js. I am seeking assistance to resolve this issue. Here is the specific problem: https://i.stack.imgur.com/FMlsL.png app.js: var express = require('express'); var app = expr ...

augmentable form that can be expanded flexibly

Perhaps I'm missing something really simple here, but I just can't seem to figure this out. I'm attempting to create a form that can dynamically extend. The issue I'm facing is that I can't get this particular code to function: & ...

extract information from an external JSON document

I have a JSON file filled with data, along with a JSX file containing a button and a div. I'm looking to extract the data from the JSON file and display it in the div when the button is clicked. However, I'm at a loss on how to achieve this. The ...

Stick your navbar to the top with Bootstrap 4

I have a bootstrap 4.1 navbar set up and I'm trying to ensure that my div "#pin_to_top" always stays at the top of the navbar. This way, on wider screens, it will be displayed as: Logo - Menu - "#pin_to_top" (all in one row) And on smaller devices, ...

When using jQuery, the content loaded with the $ajax function only displays after refreshing the page

Located on an external server is a directory containing various .html files, including one named index.html. The server has the ability to load either the folder name or foldername/index.html in its URL. Each html file within the directory loads a corresp ...

Saving user input from a React JS form to a MySQL database

Seeking assistance on how to insert name field data into a MySQL database using Express.js and the Fetch API. Any guidance on flow and project structure would be much appreciated. Thank you in advance. class NameForm extends React.Component { constru ...

create a sapper route using JSON data

Beginning with the official Sapper template, I am keen on implementing export default as recommended by eslint: export default function get(_, res) { res.writeHead(200, { 'Content-Type': 'application/json', }); res.end(conte ...

Dragging elements using the jQuery UI Draggable feature onto a designated droppable area

Struggling to implement a Draggable element on Droppable element using jQuery UI? I almost got it working, but the elements on the droppable area keep changing their positions when moved slightly. I want them to stay put, but return to their original place ...

Vue-bootstrap spinbutton form with customizable parameters

I am in need of a custom formatter function for the b-form-spinbutton component that depends on my data. I want to pass an extra argument to the :formatter-fn property in the code snippet below, but it is not working as expected. <b-form-spinbutton :for ...

How can I customize the color of a date range in the jQuery date picker?

Currently, I am using the following method to set a different color for a specific date range - April 5th to April 7th. You can view it here: http://jsfiddle.net/sickworm/dpvz52tf/ var SelectedDates = {}; SelectedDates[new Date('04/05/2015') ...

Is there anyone who can provide a straightforward solution (code) for < something basic?

I have learned a lot about programming, but I'm stuck on one thing. How can I make an image stay in place when clicked on? What I mean is that when you click and hold on the image, then move the cursor, the image moves with the cursor. What I want is ...

Steps for deploying an ejs website with winscp

I have developed a Node.js web application using ExpressJS, with the main file being app.js. Now I need to publish this website on a domain using WinSCP. However, WinSCP requires an index.html file as the starting point for publishing the site. Is there ...

Choosing the following choice using the Material-UI Select tool

Looking for a way to enhance my dropdown select from MUI in a Nextjs application by adding two arrows for navigating to the next/previous option. Here's what my code currently looks like: <StyledFormControl> <Select value={cu ...

Ways to create a border button with a matching width to the text above using CSS

Is it possible to style a border button in CSS so that the width matches the text above? I am trying to achieve a clean look for the navigation selected text, where the border-bottom aligns with the text above. Here is an example of what I have attempted ...

Creating a Mobile-friendly Sidebar: A Guide to Angular 5

I've been seeing a lot of recommendations online about using Angular Material for creating a sidebar, but I'm hesitant to install an entire library just for one component. After following the instructions provided here, I managed to develop a si ...

Is it feasible to run a function immediately following a reload?

Recently, I came across a code snippet that intrigued me: setTimeout(function() { window.location.reload(true); if ($scope.attribute.parentAttribute.id) { angular.element(document.getElementById($scope.attribute.parentAttribute.id)).click(); ...

Customizing the appearance of a local image with inline CSS using the style attribute and background-image:url

I'm having trouble loading the image Hormiga.jpg as a background for my slideshow using inline style. The url attribute doesn't seem to be applied correctly. Here is the original code where an image is loaded successfully: <div class="pa ...

Modifying the Collapse Direction of Navigation in Bootstrap 4

Is it possible to change the collapse direction of the Bootstrap 4 navbar from 'top to bottom' to 'right to left'? Here is the code snippet I am currently using: <nav class="navbar navbar-light bg-faded"> <button class="na ...

What is the proper way to declare a JavaScript variable using a hyphen symbol?

When it comes to evaluating a string like employee-count = 3, my main issue arises when dealing with variables that may not have a standard naming convention. While valid variable names pose no challenge, identifiers such as employee-count leave me slightl ...

An error is thrown when trying to use an imported function from Next.js in the getServerSideProps function, resulting in a 'Reference

In my project, I am utilizing Firebase for authentication. Once the user successfully authenticates, I store their id token in cookies. This allows me to validate the token server-side for Server-Side Rendering (SSR) whenever any request is made to a page ...