Come passi il mouse in ReactJS? - onMouseLeave non registrato durante il passaggio del mouse veloce


102

Come puoi ottenere un evento hover o un evento attivo in ReactJS quando esegui lo styling in linea?

Ho scoperto che l'approccio onMouseEnter, onMouseLeave è bacato, quindi spero che ci sia un altro modo per farlo.

In particolare, se passi il mouse su un componente molto rapidamente, viene registrato solo l'evento onMouseEnter. OnMouseLeave non si attiva mai e quindi non può aggiornare lo stato ... lasciando che il componente appaia come se fosse ancora sospeso. Ho notato la stessa cosa se provi a imitare la pseudo-classe css ": active". Se fai clic molto velocemente, verrà registrato solo l'evento onMouseDown. L'evento onMouseUp verrà ignorato ... lasciando il componente attivo.

Ecco un JSFiddle che mostra il problema: https://jsfiddle.net/y9swecyu/5/

Video di JSFiddle con problema: https://vid.me/ZJEO

Il codice:

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')
)

1
Grazie per il suggerimento. L'ho riscritto come domanda adesso.
HelpMeStackOverflowMyOnlyHope

Aggiungi alcuni esempi di codice che evidenziano il problema (idealmente come jsFiddle o equivalente) poiché non è ancora chiaro quale sia il problema. Cosa intendi per "evento registrato"?
WiredPrairie

@HelpMeStackOverflowMyOnlyHope volevi scegliere una risposta? stackoverflow.com/a/35619979/1579789 grazie!
Elon Zito,

Risposte:


92

hai provato uno di questi?

onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp

SyntheticEvent

menziona anche quanto segue:

React normalizza gli eventi in modo che abbiano proprietà coerenti su browser diversi.

I gestori di eventi seguenti vengono attivati ​​da un evento nella fase di bubbling. Per registrare un gestore di eventi per la fase di cattura, aggiungi Capture al nome dell'evento; ad esempio, invece di usare onClick, useresti onClickCapture per gestire l'evento click nella fase di cattura.


4
Solo per menzionare per onMouseEnter e onMouseLeave, dai documenti: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.
P Fuster

40

Le risposte precedenti sono piuttosto confuse. Non è necessario uno stato di reazione per risolvere questo problema, né alcuna speciale libreria esterna. Può essere ottenuto con puro css / sass:

Lo stile:

.hover {
  position: relative;

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

  &:hover &__hover {
    opacity: 1;
  }

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

  &__no-hover {
    opacity: 1;
  }
}

Il componente React

Una semplice Hoverfunzione di rendering puro:

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

Utilizzo

Quindi usalo in questo modo:

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

1
Potrebbe piacerti questa risposta: stackoverflow.com/a/50342093/101290
Beau Smith

2
Questa è la migliore risposta a questa domanda.
Strumento

20

puoi usare onMouseOver={this.onToggleOpen}e onMouseOut={this.onToggleOpen}per riflettere su un componente


Ha funzionato perfettamente per me, grazie mille. Ma come posso accedere a quelle altre funzioni come me, ad esempio, esisterebbe "onMauseOverFirstTime"? Da dove hai preso queste informazioni?
SmoggeR_js

1
@MtgKhaJeskai reactjs.org/docs/events.html Se vuoi qc come onMouseOverFirstTime dovresti crearlo tu stesso, ad esempio, fai in modo che la funzione si attivi solo quando 'firstMouseOver' nel tuo stato è true e impostalo su false quando il la funzione è stata chiamata una volta.
cubefox

Ho fondato! Grazie mille! : D
SmoggeR_js

No, non esiste la funzione "onMauseOverFirstTime"; Ma puoi usare un flag per eseguire questa azione, aggiungere questo ai tuoi stati "states: {isFirstTime: false}" e renderlo vero in "onMouseOver" @MtgKhaJeskai
Behnam Eskandari

12

Se riesci a produrre una piccola demo che mostri il bug onMouseEnter / onMouseLeave o onMouseDown / onMouseUp, varrebbe la pena pubblicarlo nella pagina dei problemi di ReactJS o nella mailing list, solo per sollevare la domanda e ascoltare cosa hanno da dire gli sviluppatori al riguardo.

Nel tuo caso d'uso, sembri sottintendere che gli stati CSS: hover e: active sarebbero sufficienti per i tuoi scopi, quindi ti suggerisco di usarli. I CSS sono ordini di grandezza più veloci e affidabili di Javascript, perché sono implementati direttamente nel browser.

Tuttavia, gli stati: hover e: active non possono essere specificati negli stili inline. Quello che puoi fare è assegnare un ID o un nome di classe ai tuoi elementi e scrivere i tuoi stili in un foglio di stile, se sono in qualche modo costanti nella tua applicazione, o in un <style>tag generato dinamicamente .

Ecco un esempio di quest'ultima tecnica: https://jsfiddle.net/ors1vos9/


@clusterBuddy Suppongo sia un bug in JsFiddle o nelle librerie, perché il codice è corretto e ha sempre funzionato bene.
Tobia

11

Nota: questa risposta era per una versione precedente di questa domanda in cui il richiedente stava cercando di utilizzare JavaScript per applicare gli stili CSS ... che può essere fatto semplicemente con CSS.

Una csssoluzione semplice .

Per l'applicazione degli stili di base, CSS è più semplice e più performante delle soluzioni JS il 99% delle volte. (Sebbene le soluzioni CSS-in-JS più moderne, ad esempio React Components, ecc., Siano probabilmente più gestibili).

Esegui questo snippet di codice per vederlo in azione ...

.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>


11
Questo non risponde alla domanda.
tsujin

@tsujin - vedere la nota sopra.
Beau Smith

more performant that JS solutions 99% of the timenon vero. leggi articoli che sfatano questo mito. hai qualche fonte?
Claudiu Creanga

@ClaudiuCreanga - Inserisci un link ad articoli che sfatano questo mito.
Beau Smith

1
@ClaudiuCreanga - Ho chiarito la frase a cui ti sei opposto, per essere più conciso.
Beau Smith

9

Mi sono appena imbattuto in questo stesso problema durante l'ascolto di eventi onMouseLeave su un pulsante disabilitato. Ci ho aggirato ascoltando l'evento mouseleave nativo su un elemento che avvolge il pulsante disabilitato.

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>
    );
}

Ecco un violino https://jsfiddle.net/qfLzkz5x/8/


Questo approccio non funzionerà, perché non c'è modo di rendere affidabile il onMouseLeave-Evento
dreampulse

Hai controllato il violino? Sembra funzionare bene per me, tranne per il fatto che l'evento viene lanciato due volte quando esci dal mouse.
Cory Danielson

Funzionerà abbastanza spesso, ma non sempre! Vedere la discussione: stackoverflow.com/questions/7448468/...
dreampulse

5

Un pacchetto chiamato styled-componentspuò risolvere questo problema in modo ELEGANTE .

Riferimento

  1. Glen Maddern - Styling React Apps con componenti in stile

Esempio

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>


2

Non puoi farlo solo con lo styling in linea. Non consigliamo di reimplementare le funzionalità CSS in JavaScript, abbiamo già un linguaggio estremamente potente e incredibilmente veloce costruito per questo caso d'uso: CSS. Quindi usalo! Made Style It per assistere.

npm install style-it --save

Sintassi funzionale ( 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;

Sintassi JSX ( 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;

1

Usa Radium !

Quello che segue è un esempio dal loro sito web:

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'
  }
};


3
Stai mostrando come applicare un effetto CSS al passaggio del mouse utilizzando gli stili in linea e non come eseguire in modo affidabile il codice quando il cursore si trova sopra l'elemento, che è ciò che l'utente ha richiesto.
Gunchars

non sono sicuro del motivo per cui la mia risposta non è accettata sulla base di questo.
fkilaiwi

2
@Gunchars " Come puoi ottenere un evento hover o un evento attivo in ReactJS quando esegui lo styling inline? ". Prima frase della domanda di OP. È praticamente esattamente quello che ha chiesto.
trixn

1

Userei onMouseOver e onMouseOut. Causa in React

Gli eventi onMouseEnter e onMouseLeave si propagano dall'elemento lasciato a quello inserito invece del normale bubbling e non hanno una fase di cattura.

Eccolo nella documentazione di React per gli eventi del mouse.


0

Ho avuto un problema simile quando è stato chiamato onMouseEnter ma a volte il corrispondente evento onMouseLeave non è stato attivato, ecco una soluzione alternativa che funziona bene per me (si basa parzialmente su 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>
        );
    }
});

Vedi su jsfiddle: http://jsfiddle.net/qtbr5cg6/1/


Perché stava accadendo (nel mio caso): Sto eseguendo un'animazione di scorrimento jQuery (attraverso $('#item').animate({ scrollTop: 0 })) quando faccio clic sull'elemento. Quindi il cursore non lascia l'elemento "naturalmente", ma durante un'animazione basata su JavaScript ... e in questo caso onMouseLeave non è stato attivato correttamente da React (React 15.3.0, Chrome 51, Desktop)


0

So che è passato un po 'di tempo da quando è stata posta questa domanda, ma mi sono imbattuto nello stesso problema di incoerenza con onMouseLeave () Quello che ho fatto è stato usare onMouseOut () per l'elenco a discesa e con il mouse per l'intero menu, è affidabile e funziona ogni volta che l'ho provato. Ho visto gli eventi qui nei documenti: https://facebook.github.io/react/docs/events.html#mouse-events ecco un esempio utilizzando 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>
  )
}

0

Personalmente uso Style It per lo stile inline in React o mantengo il mio stile separatamente in CSS o SASS file ...

Ma se sei davvero interessato a farlo in linea, guarda la libreria, condivido alcuni degli usi di seguito:

Nel componente :

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;

Uscita :

    <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>


Inoltre puoi utilizzare le variabili JavaScript con passaggio del mouse nel tuo CSS come di seguito:

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;

E il risultato come di seguito:

<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>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.