Como você passa o mouse no ReactJS? - onMouseLeave não registrado durante passagem rápida sobre


99

Como você pode obter um evento hover ou um evento ativo no ReactJS ao fazer estilização embutida?

Descobri que a abordagem onMouseEnter, onMouseLeave tem bugs, então espero que haja outra maneira de fazer isso.

Especificamente, se você passar o mouse sobre um componente muito rapidamente, apenas o evento onMouseEnter será registrado. O onMouseLeave nunca dispara e, portanto, não pode atualizar o estado ... deixando o componente parecer como se ainda estivesse sendo pairado sobre ele. Eu percebi a mesma coisa se você tentar imitar a pseudo classe css ": active". Se você clicar muito rápido, apenas o evento onMouseDown será registrado. O evento onMouseUp será ignorado ... deixando o componente aparecendo ativo.

Aqui está um JSFiddle mostrando o problema: https://jsfiddle.net/y9swecyu/5/

Vídeo do JSFiddle com problema: https://vid.me/ZJEO

O código:

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
Obrigado pela sugestão. Eu reescrevi como uma pergunta agora.
HelpMeStackOverflowMyOnlyHope

Adicione alguns exemplos de código destacando o problema (de preferência como jsFiddle ou equivalente), pois ainda não está claro qual é o problema. O que você quer dizer com "evento é registrado"?
WiredPrairie de

@HelpMeStackOverflowMyOnlyHope você queria escolher uma resposta? stackoverflow.com/a/35619979/1579789 obrigado!
Elon Zito

Respostas:


90

Você já experimentou algum desses?

onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp

SyntheticEvent

também menciona o seguinte:

O React normaliza eventos para que tenham propriedades consistentes em diferentes navegadores.

Os manipuladores de eventos abaixo são acionados por um evento na fase de bolhas. Para registrar um manipulador de eventos para a fase de captura, anexe Capture ao nome do evento; por exemplo, em vez de usar onClick, você usaria onClickCapture para manipular o evento de clique na fase de captura.


2
Apenas para mencionar para onMouseEnter e onMouseLeave, dos documentos: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

38

As respostas anteriores são muito confusas. Você não precisa de um estado de reação para resolver isso, nem de qualquer biblioteca externa especial. Isso pode ser alcançado com puro css / sass:

O estilo:

.hover {
  position: relative;

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

  &:hover &__hover {
    opacity: 1;
  }

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

  &__no-hover {
    opacity: 1;
  }
}

O Componente React

Uma Hoverfunção de renderização pura simples :

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

Uso

Em seguida, use-o assim:

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

1
Você pode gostar desta resposta: stackoverflow.com/a/50342093/101290
Beau Smith

2
Esta é a melhor resposta para esta pergunta.
Ferramenta de

18

você pode usar onMouseOver={this.onToggleOpen}e onMouseOut={this.onToggleOpen}refletir sobre o componente


Funcionou perfeitamente para mim, muito obrigado. Mas, como posso acessar outras funções como eu, por exemplo, existiriam "onMauseOverFirstTime"? De onde você tirou aquela informação?
SmoggeR_js

1
@MtgKhaJeskai reactjs.org/docs/events.html Se quiser um sth como onMouseOverFirstTime, você terá que criá-lo sozinho, por exemplo, faça a função disparar apenas quando 'firstMouseOver' em seu estado for verdadeiro e defina-o como falso quando o função foi chamada uma vez.
cubefox

Eu fundei! Muito obrigado! : D
SmoggeR_js

Não, não existe a função "onMauseOverFirstTime"; Mas você pode usar um sinalizador para fazer esta ação, adicione aos seus estados "states: {isFirstTime: false}" e torná-lo verdadeiro em "onMouseOver" @MtgKhaJeskai
Behnam Eskandari

12

Se você puder produzir uma pequena demonstração mostrando o bug onMouseEnter / onMouseLeave ou onMouseDown / onMouseUp, valeria a pena postá-lo na página de problemas do ReactJS ou na lista de e-mails, apenas para levantar a questão e ouvir o que os desenvolvedores têm a dizer sobre isso.

Em seu caso de uso, você parece sugerir que os estados CSS: hover e: active seriam suficientes para seus propósitos, então sugiro que você os use. CSS é muito mais rápido e confiável do que Javascript, porque é implementado diretamente no navegador.

No entanto, os estados: hover e: active não podem ser especificados em estilos embutidos. O que você pode fazer é atribuir um ID ou um nome de classe aos seus elementos e escrever seus estilos em uma folha de estilo, se eles forem constantes em seu aplicativo, ou em uma <style>tag gerada dinamicamente .

Aqui está um exemplo da última técnica: https://jsfiddle.net/ors1vos9/


@clusterBuddy Suponho que seja um bug no JsFiddle ou nas bibliotecas, porque o código está correto e sempre funcionou bem.
Tobia

10

Nota: Esta resposta foi para uma versão anterior desta questão onde o questionador estava tentando usar JavaScript para aplicar estilos CSS ... o que pode ser feito simplesmente com CSS.

Uma csssolução simples .

Para aplicar estilos básicos, CSS é mais simples e tem mais desempenho que as soluções JS 99% das vezes. (Embora as soluções CSS-in-JS mais modernas - por exemplo, componentes do React etc. - sejam indiscutivelmente mais fáceis de manter.)

Execute este snippet de código para vê-lo em ação ...

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


10
Isso não responde à pergunta.
tsujin 01 de

@tsujin - veja a nota acima.
Beau Smith

more performant that JS solutions 99% of the timeNão é verdade. leia artigos desmascarando esse mito. você tem alguma fonte?
Claudiu Creanga

@ClaudiuCreanga - Por favor, link para artigos desmascarando esse mito.
Beau Smith

1
@ClaudiuCreanga - Esclareci a frase à qual você se opôs, para ser mais conciso.
Beau Smith de

9

Acabei de encontrar o mesmo problema ao ouvir eventos onMouseLeave em um botão desativado. Eu trabalhei em torno disso ouvindo o evento de mouseleave nativo em um elemento que envolve o botão desativado.

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

Aqui está um violino https://jsfiddle.net/qfLzkz5x/8/


Esta abordagem não vai funcionar, porque não há nenhuma maneira de obter a confiança onMouseLeave-Evento
dreampulse

Você verificou o violino? Parece funcionar bem para mim, exceto que o evento é lançado duas vezes quando você tira o mouse.
Cory Danielson

Funcionará com bastante frequência, mas nem sempre! Veja esta discussão: stackoverflow.com/questions/7448468/…
dreampulse

5

Um pacote chamado styled-componentspode resolver este problema de maneira ELEGANTE .

Referência

  1. Glen Maddern - Apps de estilo de reação com componentes estilizados

Exemplo

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

Você não pode com o estilo embutido sozinho. Não recomendo reimplementar os recursos CSS em JavaScript, pois já temos uma linguagem extremamente poderosa e incrivelmente rápida construída para este caso de uso - CSS. Então use! Made Style It para ajudar.

npm install style-it --save

Sintaxe funcional ( 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;

Sintaxe 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

Use Radium !

A seguir está um exemplo de seu site:

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
Você está mostrando como aplicar um efeito CSS de foco usando estilos embutidos e não como executar o código de forma confiável quando o cursor está sobre o elemento, que é o que o usuário pediu.
Gunchars 01 de

não tenho certeza por que minha resposta não é aceita com base nisso.
fkilaiwi

2
@Gunchars " Como você pode obter um evento hover ou um evento ativo no ReactJS ao fazer um estilo embutido? ". Muito primeira frase da pergunta de OP. É exatamente o que ele pediu.
trixn

1

Eu usaria onMouseOver e onMouseOut. Causa em reação

Os eventos onMouseEnter e onMouseLeave se propagam do elemento que está sendo deixado para aquele que está sendo inserido, em vez do bubbling comum e não têm uma fase de captura.

Aqui está na documentação do React para eventos de mouse.


0

Tive um problema semelhante quando onMouseEnter foi chamado, mas às vezes o evento onMouseLeave correspondente não foi disparado. Esta é uma solução alternativa que funciona bem para mim (depende parcialmente do 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>
        );
    }
});

Veja no jsfiddle: http://jsfiddle.net/qtbr5cg6/1/


Por que isso estava acontecendo (no meu caso): estou executando uma animação de rolagem jQuery (através $('#item').animate({ scrollTop: 0 })) ao clicar no item. Portanto, o cursor não sai do item "naturalmente", mas durante uma animação baseada em JavaScript ... e neste caso o onMouseLeave não foi disparado corretamente pelo React (React 15.3.0, Chrome 51, Desktop)


0

Eu sei que já faz um tempo desde que esta pergunta foi feita, mas acabei de encontrar o mesmo problema de inconsistência com onMouseLeave () O que fiz foi usar onMouseOut () para a lista suspensa e, ao sair do mouse, para todo o menu, é confiável e funciona sempre que o testei. Eu vi os eventos aqui na documentação: https://facebook.github.io/react/docs/events.html#mouse-events aqui está um exemplo usando 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

Eu pessoalmente uso o Style It para o estilo inline no React ou mantenho meu estilo separadamente em um arquivo CSS ou SASS ...

Mas se você estiver realmente interessado em fazer isso in-line, olhe para a biblioteca, eu compartilho alguns dos usos abaixo:

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

Produto :

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


Além disso, você pode usar variáveis ​​JavaScript com foco em seu CSS como abaixo:

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 o resultado é o seguinte:

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

-2

Eu pessoalmente acho o uso do Emotion para gerenciar esses estilos. Emotion é uma biblioteca CSS-in-JS que pode tornar o gerenciamento de estilos React mais conveniente, dependendo de sua preferência, ao contrário de estilos embutidos como você tem em seu exemplo. Aqui você poderá encontrar mais informações sobre ele https://emotion.sh/docs/introduction espero que isso ajude você.

Aqui está um exemplo simples de emoção que adiciona um ponteiro e uma mudança de cor a um div ao passar o mouse:

const secondStyle = css`
  background: blue;
  height: 20px;
  width: 20px;
  :hover {
    cursor: pointer;
    background: red;
`

function myComponent() {
  return (
     <div className={firstStyle}>
       <button className={secondStyle}>Submit<button>
  )
}

Explique como isso resolve o problema. Não vejo nada relacionado à pergunta.
Paul Sturm
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.