ドロップダウンコンポーネントの外側でクリックが発生したときにドロップダウンメニューを閉じたいのですが。
それ、どうやったら出来るの?
回答:
要素では私が追加されているmousedown
とmouseup
、このように:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
次に、親でこれを行います:
componentDidMount: function () {
window.addEventListener('mousedown', this.pageClick, false);
},
pageClick: function (e) {
if (this.mouseIsDownOnCalendar) {
return;
}
this.setState({
showCal: false
});
},
mouseDownHandler: function () {
this.mouseIsDownOnCalendar = true;
},
mouseUpHandler: function () {
this.mouseIsDownOnCalendar = false;
}
これshowCal
は、true
私の場合はカレンダーをfalse
表示して非表示にするブール値です。
preventDefault()
、イベントをすぐに呼び出す必要があるか、ネイティブのAndroidの動作が作動し、Reactの前処理が妨げられるため、androidではうまく機能しません。それ以来、私はnpmjs.com/package/react-onclickoutsideを作成しました。
ライフサイクルメソッドを使用して、ドキュメントにイベントリスナーを追加および削除します。
React.createClass({
handleClick: function (e) {
if (this.getDOMNode().contains(e.target)) {
return;
}
},
componentWillMount: function () {
document.addEventListener('click', this.handleClick, false);
},
componentWillUnmount: function () {
document.removeEventListener('click', this.handleClick, false);
}
});
このコンポーネントの48〜54行目を確認してください。https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
イベントのターゲットを確認します。イベントがコンポーネントまたはコンポーネントの子である場合、クリックは内部にあります。それ以外の場合は外にありました。
React.createClass({
clickDocument: function(e) {
var component = React.findDOMNode(this.refs.component);
if (e.target == component || $(component).has(e.target).length) {
// Inside of the component.
} else {
// Outside of the component.
}
},
componentDidMount: function() {
$(document).bind('click', this.clickDocument);
},
componentWillUnmount: function() {
$(document).unbind('click', this.clickDocument);
},
render: function() {
return (
<div ref='component'>
...
</div>
)
}
});
これを多くのコンポーネントで使用する場合は、ミックスインの方が適しています。
var ClickMixin = {
_clickDocument: function (e) {
var component = React.findDOMNode(this.refs.component);
if (e.target == component || $(component).has(e.target).length) {
this.clickInside(e);
} else {
this.clickOutside(e);
}
},
componentDidMount: function () {
$(document).bind('click', this._clickDocument);
},
componentWillUnmount: function () {
$(document).unbind('click', this._clickDocument);
},
}
this.refs.component
0.14のようDOM要素を参照しているfacebook.github.io/react/blog/2015/07/03/...
あなたの特定のユースケースでは、現在受け入れられている答えは少しオーバーエンジニアリングされています。ユーザーがドロップダウンリストからクリックしたときにリッスンする場合は、<select>
コンポーネントを親要素として使用し、それにonBlur
ハンドラーをアタッチします。
このアプローチの唯一の欠点は、ユーザーが要素にすでにフォーカスを維持していることを前提とし、フォームコントロールに依存していることです(tab
キーが要素にもフォーカスし、ぼかすことを考慮した場合、これは必要な場合とそうでない場合があります)。)-しかし、これらの欠点は実際には、より複雑なユースケースの制限にすぎません。その場合、より複雑なソリューションが必要になる場合があります。
var Dropdown = React.createClass({
handleBlur: function(e) {
// do something when user clicks outside of this element
},
render: function() {
return (
<select onBlur={this.handleBlur}>
...
</select>
);
}
});
onBlur
divは、divがtabIndex
属性を持っている場合にも機能します
コンポーネントの外部で発生するイベント用の汎用イベントハンドラーreact-outside-eventを作成しました。
実装自体は簡単です:
window
オブジェクトにアタッチされます。onOutsideEvent
、ターゲットコンポーネントでトリガーされます。import React from 'react';
import ReactDOM from 'react-dom';
/**
* @param {ReactClass} Target The component that defines `onOutsideEvent` handler.
* @param {String[]} supportedEvents A list of valid DOM event names. Default: ['mousedown'].
* @return {ReactClass}
*/
export default (Target, supportedEvents = ['mousedown']) => {
return class ReactOutsideEvent extends React.Component {
componentDidMount = () => {
if (!this.refs.target.onOutsideEvent) {
throw new Error('Component does not defined "onOutsideEvent" method.');
}
supportedEvents.forEach((eventName) => {
window.addEventListener(eventName, this.handleEvent, false);
});
};
componentWillUnmount = () => {
supportedEvents.forEach((eventName) => {
window.removeEventListener(eventName, this.handleEvent, false);
});
};
handleEvent = (event) => {
let target,
targetElement,
isInside,
isOutside;
target = this.refs.target;
targetElement = ReactDOM.findDOMNode(target);
isInside = targetElement.contains(event.target) || targetElement === event.target;
isOutside = !isInside;
if (isOutside) {
target.onOutsideEvent(event);
}
};
render() {
return <Target ref='target' {... this.props} />;
}
}
};
コンポーネントを使用するには、上位コンポーネントを使用してターゲットコンポーネントクラス宣言をラップし、処理するイベントを定義する必要があります。
import React from 'react';
import ReactDOM from 'react-dom';
import ReactOutsideEvent from 'react-outside-event';
class Player extends React.Component {
onOutsideEvent = (event) => {
if (event.type === 'mousedown') {
} else if (event.type === 'mouseup') {
}
}
render () {
return <div>Hello, World!</div>;
}
}
export default ReactOutsideEvent(Player, ['mousedown', 'mouseup']);
うまくいかなかったのに、答えの1つに投票しました。それは私をこの解決策に導いてしまいました。操作の順序を少し変更しました。ターゲットではmouseDownを、ターゲットではmouseUpをリッスンしています。それらのいずれかがTRUEを返す場合、モーダルは閉じません。クリックが登録されるとすぐに、どこでも、これらの2つのブール値{mouseDownOnModal、mouseUpOnModal}はfalseに戻されます。
componentDidMount() {
document.addEventListener('click', this._handlePageClick);
},
componentWillUnmount() {
document.removeEventListener('click', this._handlePageClick);
},
_handlePageClick(e) {
var wasDown = this.mouseDownOnModal;
var wasUp = this.mouseUpOnModal;
this.mouseDownOnModal = false;
this.mouseUpOnModal = false;
if (!wasDown && !wasUp)
this.close();
},
_handleMouseDown() {
this.mouseDownOnModal = true;
},
_handleMouseUp() {
this.mouseUpOnModal = true;
},
render() {
return (
<Modal onMouseDown={this._handleMouseDown} >
onMouseUp={this._handleMouseUp}
{/* other_content_here */}
</Modal>
);
}
これには、すべてのコードが親ではなく子コンポーネントに依存するという利点があります。これは、このコンポーネントを再利用するときにコピーする定型コードがないことを意味します。
.backdrop
)。.target
)を要素の外側に配置し、.backdrop
スタックインデックス(z-index
)を大きくします。次に、.backdrop
要素をクリックすると、「.target
要素の外側」と見なされます。
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
ref
これを実現するためにsを使用できます。次のようなものが機能するはずです。
をref
要素に追加します。
<div ref={(element) => { this.myElement = element; }}></div>
次に、要素の外でクリックを処理する関数を次のように追加できます。
handleClickOutside(e) {
if (!this.myElement.contains(e)) {
this.setState({ myElementVisibility: false });
}
}
最後に、イベントリスナーを追加および削除して、マウントおよびアンマウントします。
componentWillMount() {
document.addEventListener('click', this.handleClickOutside, false); // assuming that you already did .bind(this) in constructor
}
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside, false); // assuming that you already did .bind(this) in constructor
}
handleClickOutside
にdocument.addEventListener()
追加することでthis
、参照を。それ以外の場合は、Uncaught ReferenceErrorが発生します。handleClickOutsideが定義されていませんcomponentWillMount()
パーティーにはとても遅れましたが、ドロップダウンの親要素にblurイベントを設定し、関連付けられたコードでドロップダウンを閉じることと、ドロップダウンが開いているかどうかを確認するマウスダウンリスナーを親要素にアタッチすることに成功しましたイベントが開いている場合はイベントの伝播を停止し、ぼかしイベントがトリガーされないようにします。
mousedownイベントはこれをバブルアップするので、子でのmousedownが親でぼかしを引き起こすのを防ぎます。
/* Some react component */
...
showFoo = () => this.setState({ showFoo: true });
hideFoo = () => this.setState({ showFoo: false });
clicked = e => {
if (!this.state.showFoo) {
this.showFoo();
return;
}
e.preventDefault()
e.stopPropagation()
}
render() {
return (
<div
onFocus={this.showFoo}
onBlur={this.hideFoo}
onMouseDown={this.clicked}
>
{this.state.showFoo ? <FooComponent /> : null}
</div>
)
}
...
e.preventDefault()は、私が推論できる限り呼び出す必要はありませんが、Firefoxは何らかの理由でそれなしではうまく機能しません。Chrome、Firefox、Safariで動作します。
優れたreact-onclickoutsideミックスインを使用します。
npm install --save react-onclickoutside
その後
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});