Saya ingin menutup menu dropdown ketika klik terjadi di luar komponen dropdown.
Bagaimana aku melakukan itu?
Saya ingin menutup menu dropdown ketika klik terjadi di luar komponen dropdown.
Bagaimana aku melakukan itu?
Jawaban:
Dalam elemen yang telah saya tambahkan mousedowndan mouseupseperti ini:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Kemudian pada orang tua saya melakukan ini:
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;
}
Ini showCaladalah boolean yang ketika truemenunjukkan dalam kasus saya kalender dan falsemenyembunyikannya.
preventDefault()acara segera atau perilaku Android asli dimulai, yang mengganggu praproses React. Saya menulis npmjs.com/package/react-onclickoutside sejak itu.
Menggunakan metode siklus hidup menambah dan menghapus event listener ke dokumen.
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);
}
});
Lihat baris 48-54 dari komponen ini: https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
Lihat target acara, jika acara langsung pada komponen, atau turunan dari komponen itu, klik ada di dalam. Kalau tidak, itu di luar.
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>
)
}
});
Jika ini akan digunakan di banyak komponen, lebih bagus dengan mixin:
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);
},
}
Lihat contoh di sini: https://jsfiddle.net/0Lshs7mg/1/
this.refs.componentmengacu pada elemen DOM pada 0.14 facebook.github.io/react/blog/2015/07/03/…
Untuk kasus penggunaan khusus Anda, jawaban yang diterima saat ini agak berlebihan. Jika Anda ingin mendengarkan saat pengguna mengklik daftar drop-down, cukup gunakan <select>komponen sebagai elemen induk dan lampirkan onBlurpenangan padanya.
Satu-satunya kelemahan dari pendekatan ini adalah bahwa ia mengasumsikan pengguna telah mempertahankan fokus pada elemen, dan itu bergantung pada kontrol bentuk (yang mungkin atau mungkin bukan yang Anda inginkan jika Anda memperhitungkan bahwa tabkuncinya juga memfokuskan dan mengaburkan elemen ) - tetapi kekurangan ini hanya benar-benar batas untuk kasus penggunaan yang lebih rumit, dalam hal ini solusi yang lebih rumit mungkin diperlukan.
var Dropdown = React.createClass({
handleBlur: function(e) {
// do something when user clicks outside of this element
},
render: function() {
return (
<select onBlur={this.handleBlur}>
...
</select>
);
}
});
onBlurdiv juga bekerja dengan div jika memiliki tabIndexatribut
Saya telah menulis event handler generik untuk event yang berasal dari luar komponen, react-outside-event .
Implementasinya sendiri sederhana:
windowobjek.onOutsideEventpada komponen target.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} />;
}
}
};
Untuk menggunakan komponen, Anda perlu membungkus deklarasi kelas komponen target menggunakan komponen urutan yang lebih tinggi dan menentukan peristiwa yang ingin Anda tangani:
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']);
Saya memilih salah satu jawaban meskipun itu tidak berhasil untuk saya. Itu akhirnya membawa saya ke solusi ini. Saya mengubah urutan operasi sedikit. Saya mendengarkan mouseDown pada target dan mouseUp pada target. Jika salah satu dari mereka mengembalikan TRUE, kami tidak menutup modal. Segera setelah klik terdaftar, di mana saja, kedua boolean itu {mouseDownOnModal, mouseUpOnModal} disetel kembali ke 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>
);
}
Ini memiliki keuntungan bahwa semua kode terletak pada komponen anak, dan bukan induknya. Artinya tidak ada kode boilerplate untuk disalin saat menggunakan kembali komponen ini.
.backdrop)..target) di luar .backdropelemen dan dengan indeks susun yang lebih besar ( z-index).Kemudian klik apa pun pada .backdropelemen tersebut akan dianggap "di luar .targetelemen".
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
Anda dapat menggunakan refs untuk mencapai ini, sesuatu seperti berikut ini akan bekerja.
Tambahkan refke elemen Anda:
<div ref={(element) => { this.myElement = element; }}></div>
Anda kemudian dapat menambahkan fungsi untuk menangani klik di luar elemen seperti:
handleClickOutside(e) {
if (!this.myElement.contains(e)) {
this.setState({ myElementVisibility: false });
}
}
Kemudian terakhir, tambahkan dan hapus event listener pada will mount dan akan unmount.
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
}
handleClickOutsidedi document.addEventListener()dengan menambahkan thisreferensi. Jika tidak, ia memberikan Uncaught ReferenceError: handleClickOutside tidak didefinisikan dalamcomponentWillMount()
Sangat terlambat ke pesta, tetapi saya telah berhasil menyetel acara buram pada elemen induk dari dropdown dengan kode terkait untuk menutup dropdown, dan juga melampirkan pendengar mousedown ke elemen induk yang memeriksa apakah dropdown terbuka atau tidak, dan akan menghentikan penyebaran acara jika terbuka sehingga acara blur tidak akan dipicu.
Karena peristiwa mousedown menggelembung, ini akan mencegah mousedown apa pun pada anak-anak yang menyebabkan buram pada induknya.
/* 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 () seharusnya tidak dipanggil sejauh yang saya bisa nalar tetapi firefox tidak berjalan baik tanpanya karena alasan apa pun. Berfungsi di Chrome, Firefox, dan Safari.
Saya menemukan cara yang lebih sederhana tentang ini.
Anda hanya perlu menambahkan onHide(this.closeFunction)modal
<Modal onHide={this.closeFunction}>
...
</Modal>
Dengan asumsi Anda memiliki fungsi untuk menutup modal.
Gunakan mixin react-onclickoutside yang sangat baik :
npm install --save react-onclickoutside
Lalu
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});