Incorporating timed hover effects in React applications

Take a look at the codesandbox example

I'm currently working on implementing a modal that appears after a delay when hovering over a specific div. However, I've encountered some challenges. For instance, if the timeout is set to 1000ms and you hover over the div but move away before the 1000ms mark, the modal still pops up. What I really want is for the modal to only appear after the full delay period if the mouse stays over the div. How can I achieve this desired effect without the unintended consequences I'm facing now? Any suggestions are appreciated!

index.tsx:

import * as React from "react";
import ReactDOM from "react-dom";
import "./styles.css";

const Modal: React.FC = () => {
  const divRef = React.useRef<HTMLDivElement>(null);
  const [showModal, setShowModal] = React.useState<boolean>(false);

  React.useEffect(() => {
    const divNode = divRef.current;

    const handleEvent = (event: Event): void => {
      if (divNode) {
        if (divNode.contains(event.target as Node)) {
          setTimeout(() => setShowModal(true), 1000);
        } else {
          setShowModal(false);
        }
      }
    };

    document.addEventListener("mouseover", handleEvent);

    return () => {
      document.removeEventListener("mouseover", handleEvent);
    };
  }, [divRef]);

  return (
    <div className="container">
      <div className="div" ref={divRef}>
        Hover Over Me
      </div>
      {showModal && <div className="modal">This is the modal</div>}
    </div>
  );
};

const App: React.FC = () => (
  <>
    <Modal />
    <Modal />
    <Modal />
    <Modal />
  </>
);

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

Answer №1

A mouse out event needs to be included in order to hide the modal.

By calling a function on the 'mouseout' event listener and setting showModal to false, the modal will be hidden whenever the mouse is moved.

setShowModal(false)

Additionally, consider setting a timeout to a variable and using clearTimeout(variable_that_set_to_timeout) on mouseout event:

 React.useEffect(() => {
    const divNode = divRef.current;
    let timeout = null;

    const handleEvent = (event: Event): void => {
      if (divNode) {
        if (divNode.contains(event.target as Node)) {
          timeout = setTimeout(() => setShowModal(true), 1000);
        } else {
          setShowModal(false);
        }
      }
    };

    const hideModal = (event: Event): void => {
      clearTimeout(timeout);
      setShowModal(false);
    };

    divNode.addEventListener("mouseover", handleEvent);

    divNode.addEventListener("mouseout", hideModal);

    return () => {
      document.removeEventListener("mouseover", handleEvent);
    };
  }, [divRef]);

Visit this sandbox link for more details.

Answer №2

It's advisable to refrain from manipulating the DOM directly in React as it differs from jQuery. Here is an alternative approach for creating a modal component:

const Modal: React.FC = () => {
  const [timeout, setModalTimeout] = React.useState(null);
  const [showModal, setShowModal] = React.useState<boolean>(false);
  return (
    <div className="container">
      <div className="div" onMouseEnter={() => {
         timeout && !showModal && clearTimeout(timeout);
         setModalTimeout(setTimeout(() => setShowModal(true), 1000))
      }} onMouseLeave={() => {
        timeout && clearTimeout(timeout)
        setShowModal(false);
      }}>
        Hover Me
      </div>
      {showModal && <div className="modal">modal</div>}
    </div>
  );
};

References:

Answer №3

To handle this situation effectively, it is recommended to develop a custom useTimeout hook and oversee the state of the hover action.

import { useState } from "react";
import useTimeout from "./useTimeout";

export default function App() {
  const [visible, setVisible] = useState(false);
  const [hovered, setHovered] = useState(false);

  //close after 3s
  useTimeout(() => setVisible(true), !visible && hovered ? 3000 : null);

  return (
    <div className="App">
      <h1>Hover Timeout Example</h1>
      <div
        onMouseEnter={() => setHovered(true)}
        onMouseLeave={() => setHovered(false)}
      >
        Hover me for 3s to show modal
        <div>Hover status: {hovered ? "true" : "false"}</div>
      </div>
      
      {visible && (
        <div>
          <h1>Modal</h1>
          <div>
            <button onClick={() => setVisible(false)}>close</button>
          </div>
        </div>
      )}
    </div>
  );
}

Code Sandbox

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

Utilizing React and Yup Validation efficiently (No need for Formik)

Is there a way to implement React input validation using Yup without Formik? I've been searching online but couldn't find any good examples. Thanks in advance! ...

What happens if setTimeout fails due to a page error?

My current setup involves using setTimeout to refresh the page, updating the jQuery template items. Here is a snippet of the relevant code: <script type="text/javascript"> var results = JSON.parse('@svgPath'.replace(/&quot;/g ...

Encountering an issue while trying to import the D3.js library into my React.js application

I'm having trouble with properly importing the d3.js libraries in my react.js app. I am using import * as d3 from 'd3' to import everything and store it in the d3 namespace, but I keep getting an error that says - Cannot read property ' ...

How to customize the arrow color of an expanded parent ExpansionPanel in material-ui

Currently facing a challenge in customizing material-ui themes to achieve the desired functionality. The goal is to have the expansion panels display a different arrow color when expanded for improved visibility. However, this customization should be appl ...

What is the reason for the .foo a:link, .foo a:visited {} selector taking precedence over the a:hover, a:active {} selector in CSS?

Sample code: http://jsfiddle.net/RuQNP/ <!DOCTYPE html> <html> <head> <title>Foo</title> <style type="text/css"> a:link, a:visited { color: blue; } a:hover, a:active { ...

Show every item from a collection on individual lines within an Angular2 module

I am working with an Angular2 component that is responsible for displaying a list of speakers stored in some data. Currently, when I add the code below to my xyz.component.html, it shows the list as comma-separated strings. However, I would like each speak ...

Local font not applying styles in Tailwind CSS

I integrated the Gilroy font into my application, but I am facing issues with tailwindcss not being able to style the text properly. The font appears too thin in all elements such as paragraphs and headers. Here is the file structure for reference: https: ...

Unsuccessful attempt at aborting an Ajax request

Currently, I have developed a basic live search feature using jQuery ajax to search through a JSON file on the server and display a list of events. The script is programmed to show a list of events that were previously displayed on the page if the search ...

Tips for testing "defineAsyncComponent" in Vue 3:

Suppose I need to unit test this utility function. I am utilizing Vue 3, however, this code resides in a "regular" JavaScript file rather than an SFC. How can I go about doing that? function getDynamicComponent() { if (...) { return defineAsyncComp ...

Is it possible that the images are unable to load on the page

The frontend code successfully retrieves the image links sent by the backend but encounters issues displaying them. Despite confirming that the imgUrl data is successfully fetched without any hotlink protection problems, the images are still not appearing ...

Having difficulty targeting the span element in my CSS code

I've got an HTML document that includes multiple span elements. <span>Skip to content</span> There are several of these span elements throughout the document. I've attempted to hide them using the following CSS: span {display: non ...

What is the best way to horizontally center an image in Bootstrap 4 while having a fixed-top navbar and preventing vertical scrollbars?

I am struggling to achieve the desired outcome, which is depicted in this image: https://i.sstatic.net/i6X0j.jpg Specific requirements for this project include: The image must be responsive A fixed-top navbar should remain visible No vertical scrolling ...

Is there a way to access and invoke a exposed function of a Vue component within a default slot?

Exploring the realms of a vue playground. The functions interfaceFunction in both ChildA and ChildB are exposed. In App, these functions can be called by obtaining references to the components that expose them. This allows direct function calls from with ...

Tips for coding in Material-UI version 5: Utilizing the color prop in the Chip component by specifying

Is there a better way to type the MUI Chip prop color when the actual value comes from an object? Using any doesn't seem like a good option. Additionally, is keyof typeof CHIP_COLORS the correct approach for typing? import { Chip, Stack } from "@ ...

Using Javascript to eliminate divs on a page that have the attribute display:none

Looking for a way to remove generated divs with display: none using JavaScript? Let's find a solution. <div id="workarea"> <div id="message" class="messages" style="display: none;">Your message was saved</div> <div id="message" c ...

Configuring the data source for an autocomplete feature in ReactJS Material UI

For one of my React components, I am utilizing the Material UI autocomplete feature. The data source is retrieved from the server asynchronously and has the following structure: const dataSource = [{ id: 001 firstName: 'fname', lastName: &apo ...

Bringing someone else's codebase up to date from version 49 to the latest version

After fixing the naming errors, I'm still encountering some issues. You can view the expected page layout here: Here is my current progress: There seems to be some glitched triangles when scrolling, and I believe splitting face4's into two fac ...

The Vue.js component fails to display when included within another component

I am experiencing an issue with a menu item not displaying properly in my Vue.js application. The menu item should display the text "This is a menu item", but for some reason it is not being processed and rendered by Vue. These are the main com ...

Retrieve the earliest and latest dates from a JSON file to utilize with FlatPicker

I have a file in an unknown format, possibly JSON, with dates listed. I am attempting to extract the minimum and maximum dates from this data in MM/DD/YYYY format for use as variables in Flatpicker's minDate and maxDate options. The current AJAX call ...

The Transformicons by Navicon featuring a stylish blue outline

While experimenting with the navicon from sara soueidan, I noticed a blue selector around the icons that I can't seem to get rid of. Is this something specific to the method used, or just a misunderstanding of jQuery? Here is the code snippet: http:/ ...