How do you Hover in ReactJS? - onMouseLeave not registered during fast hover over
JavascriptCssReactjsMouseeventDom EventsJavascript 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
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.
css
-only solution.
A simple 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
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:
-
Create a
CSS
file unless you haveindex.css
-
Create a class with a
hover
pseudo-class enabled.hover__effect:hover {}
-
Add the effects you require
.hover__effect:hover { background-color: rgb(255, 255, 255); color: rgb(0, 0, 0); }
-
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.