Как вы зависаете в ReactJS? - onMouseLeave не регистрируется при быстром наведении


102

Как вы можете добиться в ReactJS события зависания или активного события, когда вы выполняете встроенные стили?

Я обнаружил, что подход onMouseEnter, onMouseLeave содержит ошибки, поэтому надеюсь, что есть другой способ сделать это.

В частности, если вы очень быстро наведете курсор мыши на компонент, будет зарегистрировано только событие onMouseEnter. OnMouseLeave никогда не срабатывает и, следовательно, не может обновлять состояние ... оставляя компонент так, как будто он все еще находится над ним. Я заметил то же самое, если вы попытаетесь имитировать псевдокласс css ": active". Если щелкнуть очень быстро, будет зарегистрировано только событие onMouseDown. Событие onMouseUp будет проигнорировано ... компонент останется активным.

Вот JSFiddle, показывающий проблему: https://jsfiddle.net/y9swecyu/5/

Видео JSFiddle с проблемой: https://vid.me/ZJEO

Код:

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
Спасибо за предложение. Переписал сейчас как вопрос.
HelpMeStackOverflowMyOnlyHope

Пожалуйста, добавьте несколько примеров кода, выделяющих проблему (в идеале, как jsFiddle или эквивалент), так как все еще не ясно, в чем проблема. Что вы имеете в виду под «событием зарегистрировано»?
WiredPrairie

@HelpMeStackOverflowMyOnlyHope вы хотите выбрать ответ? stackoverflow.com/a/35619979/1579789 спасибо!
Илон Зито

Ответы:


92

Вы пробовали что-нибудь из этого?

onMouseDown onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp

SyntheticEvent

также упоминается следующее:

React нормализует события, чтобы они имели согласованные свойства в разных браузерах.

Обработчики событий ниже запускаются событием в фазе восходящей цепочки. Чтобы зарегистрировать обработчик событий для фазы захвата, добавьте Capture к имени события; например, вместо использования onClick вы должны использовать onClickCapture для обработки события щелчка на этапе захвата.


4
Просто чтобы упомянуть onMouseEnter и onMouseLeave из документов: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

Предыдущие ответы довольно сбивают с толку. Для решения этой проблемы вам не нужно ни состояние реакции, ни специальная внешняя библиотека. Этого можно добиться с помощью чистого css / sass:

Стиль:

.hover {
  position: relative;

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

  &:hover &__hover {
    opacity: 1;
  }

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

  &__no-hover {
    opacity: 1;
  }
}

Компонент React

Простая Hoverфункция чистого рендеринга:

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

Применение

Тогда используйте это так:

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

1
Вам может понравиться этот ответ: stackoverflow.com/a/50342093/101290
Бо Смит

2
Это лучший ответ на этот вопрос.
Tool

20

вы можете использовать onMouseOver={this.onToggleOpen}и onMouseOut={this.onToggleOpen}размышлять над компонентом


У меня это сработало отлично, большое спасибо. Но как я могу получить доступ к другим функциям, например, существующим "onMauseOverFirstTime"? Откуда вы взяли эту информацию?
SmoggeR_js

1
@MtgKhaJeskai reactjs.org/docs/events.html Если вы хотите что-то вроде onMouseOverFirstTime, вам придется создать его самостоятельно, например, сделайте так, чтобы функция срабатывала только тогда, когда 'firstMouseOver' в вашем состоянии истинно, и установите для нее значение false, когда функция вызывалась один раз.
cubefox

Я нашел! Большое спасибо! : D
SmoggeR_js

Нет, не существует функции onMauseOverFirstTime; Но вы можете использовать флаг для выполнения этого действия, добавить это к своим состояниям «states: {isFirstTime: false}» и сделать его истинным в «onMouseOver» @MtgKhaJeskai
Бехнам Эскандари

12

Если вы можете создать небольшую демонстрацию, показывающую ошибку onMouseEnter / onMouseLeave или onMouseDown / onMouseUp, было бы целесообразно опубликовать ее на странице проблем или в списке рассылки ReactJS, просто чтобы поднять вопрос и услышать, что разработчики говорят по этому поводу.

В вашем случае использования вы, кажется, подразумеваете, что состояний CSS: hover и: active будет достаточно для ваших целей, поэтому я предлагаю вам использовать их. CSS на порядки быстрее и надежнее, чем Javascript, потому что он напрямую реализован в браузере.

Однако состояния: hover и: active нельзя указать во встроенных стилях. Что вы можете сделать, так это присвоить вашим элементам идентификатор или имя класса и записать свои стили либо в таблице стилей, если они в некоторой степени постоянны в вашем приложении, либо в динамически сгенерированном<style> теге.

Вот пример последнего метода: https://jsfiddle.net/ors1vos9/


@clusterBuddy Я полагаю, что это ошибка в JsFiddle или в библиотеках, потому что код правильный и всегда работал нормально.
Tobia

11

Примечание. Этот ответ был для предыдущей версии этого вопроса, в которой задающий вопрос пытался использовать JavaScript для применения стилей css… что можно просто сделать с помощью CSS.

Просто css решение.

Для применения базовых стилей CSS в 99% случаев проще и производительнее, чем решения JS. (Хотя более современные решения CSS-in-JS - например, компоненты React и т. Д. - возможно, более удобны в обслуживании.)

Запустите этот фрагмент кода, чтобы увидеть его в действии…

.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
Это не отвечает на вопрос.
tsujin 01

@tsujin - см. примечание выше.
Beau Smith

more performant that JS solutions 99% of the timeне правда. читайте статьи, развенчивающие этот миф. у вас есть источник?
Claudiu

@ClaudiuCreanga - Пожалуйста, дайте ссылку на статьи, развенчивающие этот миф.
Бо Смит

1
@ClaudiuCreanga - Я уточнил предложение, против которого вы возражали, чтобы быть более кратким.
Бо Смит

9

Я только что столкнулся с той же проблемой при прослушивании событий onMouseLeave на отключенной кнопке. Я работал над этим, прослушивая собственное событие mouseleave в элементе, который оборачивает отключенную кнопку.

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

Вот скрипка https://jsfiddle.net/qfLzkz5x/8/


Этот подход не сработает, потому что невозможно получить надежное onMouseLeaveсобытие -Event
dreampulse

Вы проверили скрипку? Кажется, у меня все работает нормально, за исключением того, что событие генерируется дважды, когда вы наводите указатель мыши.
Кори Дэниэлсон

Будет работать довольно часто, но не всегда! См. Это обсуждение: stackoverflow.com/questions/7448468/…
dreampulse

5

Названный пакет styled-componentsможет решить эту проблему ЭЛЕГАНТНЫМ способом.

Справка

  1. Глен Мэддерн - Стилизация приложений на React с помощью стилизованных компонентов

пример

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

Вы не можете использовать только встроенный стиль. Не рекомендуется повторно реализовывать функции CSS в JavaScript. У нас уже есть язык, который является чрезвычайно мощным и невероятно быстрым для этого варианта использования - CSS. Так что используйте это! Сделано Style It, чтобы помочь.

npm install style-it --save

Функциональный синтаксис ( 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 ( 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

Используйте радий !

Ниже приводится пример с их веб-сайта:

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
Вы показываете, как применить эффект CSS при наведении курсора, используя встроенные стили, а не как надежно запустить код, когда курсор находится над элементом, что и просил пользователь.
Gunchars 01

не уверен, почему мой ответ не принят на основании этого.
fkilaiwi

2
@Gunchars « Как вы можете добиться в ReactJS события зависания или активного события, когда вы выполняете встроенные стили? ». Самое первое предложение вопроса ОП. Это в значительной степени именно то, о чем он просил.
trixn

1

Я бы использовал onMouseOver и onMouseOut. Причина в React

События onMouseEnter и onMouseLeave распространяются от оставленного элемента к элементу, который вводится, вместо обычного всплывания и не имеют фазы захвата.

Вот он в документации React для событий мыши.


0

У меня была аналогичная проблема при вызове onMouseEnter, но иногда соответствующее событие onMouseLeave не запускалось, вот обходной путь, который хорошо работает для меня (он частично зависит от 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>
        );
    }
});

Смотрите на jsfiddle: http://jsfiddle.net/qtbr5cg6/1/


Почему это произошло (в моем случае): я запускаю анимацию прокрутки jQuery (через $('#item').animate({ scrollTop: 0 })) при нажатии на элемент. Таким образом, курсор не покидает элемент «естественным образом», а во время анимации, управляемой JavaScript ... и в этом случае onMouseLeave не был запущен React должным образом (React 15.3.0, Chrome 51, Desktop)


0

Я знаю. Прошло некоторое время с тех пор, как этот вопрос был задан, но я просто столкнулся с той же проблемой несогласованности с onMouseLeave (). Я использовал onMouseOut () для раскрывающегося списка и при отпускании мыши для всего меню, это надежен и работает каждый раз, когда тестировал. Я видел события здесь, в документации: https://facebook.github.io/react/docs/events.html#mouse-events вот пример использования 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

Я лично использую Style It для встроенного стиля в React или сохраняю свой стиль отдельно в CSS или SASS. файле ...

Но если вам действительно интересно сделать это встроенным, посмотрите библиотеку, я делюсь некоторыми из приведенных ниже способов использования:

В компоненте :

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;

Выход :

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


Также вы можете использовать переменные JavaScript с наведением курсора в CSS, как показано ниже:

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;

И результат, как показано ниже:

<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>
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.