How do you Hover in ReactJS? - onMouseLeave not registered during fast hover over

JavascriptCssReactjsMouseeventDom Events

Javascript Problem Overview


How can you achieve either a hover event or active event in ReactJS when you do inline styling?

I've found that the onMouseEnter, onMouseLeave approach is buggy, so hoping there is another way to do it.

Specifically, if you mouse over a component very quickly, only the onMouseEnter event is registered. The onMouseLeave never fires, and thus can't update state... leaving the component to appear as if it still is being hovered over. I've noticed the same thing if you try and mimic the ":active" css pseudo-class. If you click really fast, only the onMouseDown event will register. The onMouseUp event will be ignored... leaving the component appearing active.

Here is a JSFiddle showing the problem: https://jsfiddle.net/y9swecyu/5/

Video of JSFiddle with problem: https://vid.me/ZJEO

The code:

var Hover = React.createClass({
    getInitialState: function() {
        return {
            hover: false
        };
    },
    onMouseEnterHandler: function() {
        this.setState({
            hover: true
        });
        console.log('enter');
    },
    onMouseLeaveHandler: function() {
        this.setState({
            hover: false
        });
        console.log('leave');
    },
    render: function() {
        var inner = normal;
        if(this.state.hover) {
            inner = hover;
        }

        return (
            <div style={outer}>
                <div style={inner}
                    onMouseEnter={this.onMouseEnterHandler}
                    onMouseLeave={this.onMouseLeaveHandler} >
                    {this.props.children}
                </div>
            </div>
        );
    }
});

var outer = {
    height: '120px',
    width: '200px',
    margin: '100px',
    backgroundColor: 'green',
    cursor: 'pointer',
    position: 'relative'
}

var normal = {
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'red',
    opacity: 0
}

var hover = {
    position: 'absolute',
    top: 0,
    bottom: 0,
    left: 0,
    right: 0,
    backgroundColor: 'red',
    opacity: 1
}

React.render(
    <Hover></Hover>,         
    document.getElementById('container')
)

Javascript Solutions


Solution 1 - Javascript

Have you tried any of these?

onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp

SyntheticEvent

it also mentions the following:

React normalizes events so that they have consistent properties across different browsers.

The event handlers below are triggered by an event in the bubbling phase. To register an event handler for the capture phase, append Capture to the event name; for example, instead of using onClick, you would use onClickCapture to handle the click event in the capture phase.

Solution 2 - Javascript

The previous answers are pretty confusing. You don't need a react-state to solve this, nor any special external lib. It can be achieved with pure css/sass:

The style:

.hover {
  position: relative;

  &:hover &__no-hover {
    opacity: 0;
  }

  &:hover &__hover {
    opacity: 1;
  }

  &__hover {
    position: absolute;
    top: 0;
    opacity: 0;
  }

  &__no-hover {
    opacity: 1;
  }
}

The React-Component

A simple Hover Pure-Rendering-Function:

const Hover = ({ onHover, children }) => (
    <div className="hover">
        <div className="hover__no-hover">{children}</div>
        <div className="hover__hover">{onHover}</div>
    </div>
)

Usage

Then use it like this:

    <Hover onHover={<div> Show this on hover </div>}>
        <div> Show on no hover </div>
    </Hover>

Solution 3 - Javascript

you can use onMouseOver={this.onToggleOpen} and onMouseOut={this.onToggleOpen} to muse over and out on component

Solution 4 - Javascript

> Note: This answer was for a previous version of this question where the question asker was trying to use JavaScript to apply css styles… which can simply be done with CSS.

A simple css-only solution.

For applying basic styles, CSS is simpler and more performant that JS solutions 99% of the time. (Though more modern CSS-in-JS solutions — eg. React Components, etc — are arguably more maintainable.)

Run this code snippet to see it in action…

.hover-button .hover-button--on,
.hover-button:hover .hover-button--off {
  display: none;
}

.hover-button:hover .hover-button--on {
  display: inline;
}

<button class='hover-button'>
  <span class='hover-button--off'>Default</span>
  <span class='hover-button--on'>Hover!</span>
</button>

Solution 5 - Javascript

If you can produce a small demo showing the onMouseEnter / onMouseLeave or onMouseDown / onMouseUp bug, it would be worthwhile to post it to ReactJS's issues page or mailing list, just to raise the question and hear what the developers have to say about it.

In your use case, you seem to imply that CSS :hover and :active states would be enough for your purposes, so I suggest you use them. CSS is orders of magnitude faster and more reliable than Javascript, because it's directly implemented in the browser.

However, :hover and :active states cannot be specified in inline styles. What you can do is assign an ID or a class name to your elements and write your styles either in a stylesheet, if they are somewhat constant in your application, or in a dynamically generated <style> tag.

Here's an example of the latter technique: https://jsfiddle.net/ors1vos9/

Solution 6 - Javascript

I've just bumped into this same problem when listening for onMouseLeave events on a disabled button. I worked around it by listening for the native mouseleave event on an element that wraps the disabled button.

componentDidMount() {
	this.watchForNativeMouseLeave();
},
componentDidUpdate() {
	this.watchForNativeMouseLeave();
},
// onMouseLeave doesn't work well on disabled elements
// https://github.com/facebook/react/issues/4251
watchForNativeMouseLeave() {
    this.refs.hoverElement.addEventListener('mouseleave', () => {
        if (this.props.disabled) {
            this.handleMouseOut();
        }
    });
},
render() {
	return (
		<span ref='hoverElement'
			onMouseEnter={this.handleMouseEnter}
			onMouseLeave={this.handleMouseLeave}
		>
			<button disabled={this.props.disabled}>Submit</button>
		</span>
	);
}

Here's a fiddle https://jsfiddle.net/qfLzkz5x/8/

Solution 7 - Javascript

I'd use onMouseOver & onMouseOut. Cause in React:

> The onMouseEnter and onMouseLeave events propagate from the element being left to the one being entered instead of ordinary bubbling and do not have a capture phase.

Here it is in the React documentation for mouse events.

Solution 8 - Javascript

A package called styled-components can solve this problem in an ELEGANT way.

Reference

  1. Glen Maddern - Styling React Apps with Styled Components

Example

const styled = styled.default
const Square = styled.div`
  height: 120px;
  width: 200px;
  margin: 100px;
  background-color: green;
  cursor: pointer;
  position: relative;
  &:hover {
    background-color: red;
  };
`
class Application extends React.Component {
  render() {
    return (
      <Square>
      </Square>
    )
  }
}

/*
 * Render the above component into the div#app
 */
ReactDOM.render(<Application />, document.getElementById('app'));

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<script src="https://unpkg.com/styled-components/dist/styled-components.min.js"></script>
<div id='app'></div>

Solution 9 - Javascript

You can't with inline styling alone. Do not recommend reimplementing CSS features in JavaScript we already have a language that is extremely powerful and incredibly fast built for this use case -- CSS. So use it! Made Style It to assist.

npm install style-it --save

Functional Syntax (JSFIDDLE)

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return Style.it(`
      .intro:hover {
        color: red;
      }

    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }
}

export default Intro;

JSX Syntax (JSFIDDLE)

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return (
      <Style>
      {`
        .intro:hover {
          color: red;
        }
      `}

      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    </Style>
  }
}

export default Intro;

Solution 10 - Javascript

Use Radium!

The following is an example from their website:

var Radium = require('radium');
var React = require('react');
var color = require('color');

@Radium
class Button extends React.Component {
  static propTypes = {
    kind: React.PropTypes.oneOf(['primary', 'warning']).isRequired
  };

  render() {
    // Radium extends the style attribute to accept an array. It will merge
    // the styles in order. We use this feature here to apply the primary
    // or warning styles depending on the value of the `kind` prop. Since its
    // all just JavaScript, you can use whatever logic you want to decide which
    // styles are applied (props, state, context, etc).
    return (
      <button
        style={[
          styles.base,
          styles[this.props.kind]
        ]}>
        {this.props.children}
      </button>
    );
  }
}

// You can create your style objects dynamically or share them for
// every instance of the component.
var styles = {
  base: {
    color: '#fff',

    // Adding interactive state couldn't be easier! Add a special key to your
    // style object (:hover, :focus, :active, or @media) with the additional rules.
    ':hover': {
      background: color('#0074d9').lighten(0.2).hexString()
    }
  },

  primary: {
    background: '#0074D9'
  },

  warning: {
    background: '#FF4136'
  }
};

Solution 11 - Javascript

I had a similar issue when onMouseEnter was called but sometimes the corresponding onMouseLeave event wasn't fired, here is a workaround that works well for me (it partially relies on jQuery):

var Hover = React.createClass({
    getInitialState: function() {
        return {
            hover: false
        };
    },
    onMouseEnterHandler: function(e) {
        this.setState({
            hover: true
        });
        console.log('enter');

        $(e.currentTarget).one("mouseleave", function (e) {
            this.onMouseLeaveHandler();
        }.bind(this));

    },
    onMouseLeaveHandler: function() {
        this.setState({
            hover: false
        });
        console.log('leave');
    },
    render: function() {
        var inner = normal;
        if(this.state.hover) {
            inner = hover;
        }

        return (
            <div style={outer}>
                <div style={inner}
                    onMouseEnter={this.onMouseEnterHandler} >
                    {this.props.children}
                </div>
            </div>
        );
    }
});

See on jsfiddle: http://jsfiddle.net/qtbr5cg6/1/


Why was it happening (in my case): I am running a jQuery scrolling animation (through $('#item').animate({ scrollTop: 0 })) when clicking on the item. So the cursor doesn't leave the item "naturally", but during a the JavaScript-driven animation ... and in this case the onMouseLeave was not fired properly by React (React 15.3.0, Chrome 51, Desktop)

Solution 12 - Javascript

I know It's been a while since this question was asked but I just run into the same issue of inconsistency with onMouseLeave() What I did is to use onMouseOut() for the drop-list and on mouse leave for the whole menu, it is reliable and works every time I've tested it. I saw the events here in the docs: https://facebook.github.io/react/docs/events.html#mouse-events here is an example using https://www.w3schools.com/bootstrap/bootstrap_dropdowns.asp:

handleHoverOff(event){
  //do what ever, for example I use it to collapse the dropdown
  let collapsing = true;
  this.setState({dropDownCollapsed : collapsing });
}

render{
  return(
    <div class="dropdown" onMouseLeave={this.handleHoverOff.bind(this)}>
      <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Dropdown Example
      <span class="caret"></span></button>
      <ul class="dropdown-menu" onMouseOut={this.handleHoverOff.bind(this)}>
        <li><a href="#">bla bla 1</a></li>
        <li><a href="#">bla bla 2</a></li>
        <li><a href="#">bla bla 3</a></li>
      </ul>
    </div>
  )
}

Solution 13 - Javascript

I personally use Style It for inline-style in React or keep my style separately in a CSS or SASS file...

But if you are really interested doing it inline, look at the library, I share some of the usages below:

In the component:

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    return (
      <Style>
        {`
          .intro {
            font-size: 40px;
          }
        `}
      
        <p className="intro">CSS-in-JS made simple -- just Style It.</p>
      </Style>
    );
  }
}

export default Intro;

Output:

    <p class="intro _scoped-1">
      <style type="text/css">
        ._scoped-1.intro {
          font-size: 40px;
        }
      </style>
    
      CSS-in-JS made simple -- just Style It.
    </p>


Also you can use JavaScript variables with hover in your CSS as below :

import React from 'react';
import Style from 'style-it';

class Intro extends React.Component {
  render() {
    const fontSize = 13;

    return Style.it(`
      .intro {
        font-size: ${ fontSize }px;  // ES2015 & ES6 Template Literal string interpolation
      }
      .package {
        color: blue;
      }
      .package:hover {
        color: aqua;
      }
    `,
      <p className="intro">CSS-in-JS made simple -- just Style It.</p>
    );
  }
}

export default Intro;

And the result as below:

<p class="intro _scoped-1">
  <style type="text/css">
    ._scoped-1.intro {
      font-size: 13px;
    }
    ._scoped-1 .package {
      color: blue;
    }
    ._scoped-1 .package:hover {
      color: aqua;
    }
  </style>

  CSS-in-JS made simple -- just Style It.
</p>

Solution 14 - Javascript

Hover is a CSS feature; it comes with the CSS side of the app so do it the right way is so much fun. Simply put, when I require a hover effect on an element in the React app, first, I create a class in my CSS file and then I add the created class into the className of the element. The steps I follow are:

  1. Create a CSS file unless you have index.css

  2. Create a class with a hover pseudo-class enabled

    .hover__effect:hover {}

  3. Add the effects you require

    .hover__effect:hover { background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); }

  4. Then add the hover__effect to the class's component that should be different when the mouse pointer hovers over it

Please check my sandbox for a live demo.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHelpMeStackOverflowMyOnlyHopeView Question on Stackoverflow
Solution 1 - JavascriptElon ZitoView Answer on Stackoverflow
Solution 2 - JavascriptdreampulseView Answer on Stackoverflow
Solution 3 - JavascriptBehnam EskandariView Answer on Stackoverflow
Solution 4 - JavascriptBeau SmithView Answer on Stackoverflow
Solution 5 - JavascriptTobiaView Answer on Stackoverflow
Solution 6 - JavascriptCory DanielsonView Answer on Stackoverflow
Solution 7 - Javascripthlkughl View Answer on Stackoverflow
Solution 8 - JavascriptKarl XuView Answer on Stackoverflow
Solution 9 - JavascriptJoshua RobinsonView Answer on Stackoverflow
Solution 10 - JavascriptfkilaiwiView Answer on Stackoverflow
Solution 11 - JavascriptkunnixView Answer on Stackoverflow
Solution 12 - JavascriptRamiroIsBackView Answer on Stackoverflow
Solution 13 - JavascriptAlirezaView Answer on Stackoverflow
Solution 14 - JavascriptEsmaeil MIRZAEEView Answer on Stackoverflow