Tôi muốn đóng menu thả xuống khi nhấp chuột xảy ra bên ngoài thành phần thả xuống.
Làm thế nào để làm điều đó?
Tôi muốn đóng menu thả xuống khi nhấp chuột xảy ra bên ngoài thành phần thả xuống.
Làm thế nào để làm điều đó?
Câu trả lời:
Trong phần tử tôi đã thêm mousedown
và mouseup
như thế này:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Sau đó, trong cha mẹ, tôi làm điều này:
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;
}
Đây showCal
là một boolean khi true
hiển thị trong trường hợp của tôi một lịch và false
ẩn nó.
preventDefault()
các sự kiện ngay lập tức hoặc hành vi Android gốc bắt đầu, điều mà quá trình xử lý trước của React can thiệp vào. Tôi đã viết npmjs.com/package/react-onclickoutside kể từ đó.
Sử dụng các phương pháp vòng đời sẽ thêm và xóa các trình xử lý sự kiện vào tài liệu.
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);
}
});
Kiểm tra các dòng 48-54 của thành phần này: https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
Nhìn vào mục tiêu của sự kiện, nếu sự kiện nằm trực tiếp trên thành phần hoặc con của thành phần đó, thì nhấp chuột ở bên trong. Nếu không thì nó đã ở bên ngoài.
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>
)
}
});
Nếu điều này được sử dụng trong nhiều thành phần, nó sẽ đẹp hơn với hỗn hợp:
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);
},
}
Xem ví dụ tại đây: https://jsfiddle.net/0Lshs7mg/1/
this.refs.component
đang đề cập đến phần tử DOM kể từ 0,14 facebook.github.io/react/blog/2015/07/03/…
Đối với trường hợp sử dụng cụ thể của bạn, câu trả lời hiện được chấp nhận là một chút được thiết kế quá mức. Nếu bạn muốn lắng nghe khi người dùng nhấp ra khỏi danh sách thả xuống, chỉ cần sử dụng một <select>
thành phần làm phần tử mẹ và đính kèm một onBlur
trình xử lý vào nó.
Hạn chế duy nhất của phương pháp này là nó giả định rằng người dùng đã duy trì sự tập trung vào phần tử và nó phụ thuộc vào một điều khiển biểu mẫu (có thể có hoặc không phải những gì bạn muốn nếu bạn tính đến rằng tab
khóa cũng lấy nét và làm mờ phần tử ) - nhưng những hạn chế này chỉ thực sự là một giới hạn cho các trường hợp sử dụng phức tạp hơn, trong trường hợp đó, một giải pháp phức tạp hơn có thể là cần thiết.
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 cũng làm việc với div nếu nó có tabIndex
thuộc tính
Tôi đã viết một trình xử lý sự kiện chung cho các sự kiện bắt nguồn bên ngoài thành phần, phản ứng-bên ngoài-sự kiện .
Bản thân việc thực hiện rất đơn giản:
window
đối tượng.onOutsideEvent
thành phần đích.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} />;
}
}
};
Để sử dụng thành phần, bạn cần bao bọc khai báo lớp thành phần đích bằng thành phần bậc cao hơn và xác định các sự kiện mà bạn muốn xử lý:
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']);
Tôi đã bình chọn một trong những câu trả lời mặc dù nó không phù hợp với tôi. Nó đã dẫn tôi đến giải pháp này. Tôi đã thay đổi thứ tự hoạt động một chút. Tôi lắng nghe mouseDown trên mục tiêu và mouseUp trên mục tiêu. Nếu một trong hai điều đó trả về TRUE, chúng tôi không đóng phương thức. Ngay sau khi một nhấp chuột được đăng ký, ở bất kỳ đâu, hai boolean đó {mouseDownOnModal, mouseUpOnModal} được đặt lại thành 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>
);
}
Điều này có lợi thế là tất cả mã nằm trong thành phần con chứ không phải thành phần mẹ. Nó có nghĩa là không có mã soạn sẵn để sao chép khi sử dụng lại thành phần này.
.backdrop
)..target
) bên ngoài .backdrop
phần tử và có chỉ số xếp chồng lớn hơn ( z-index
).Sau đó, bất kỳ nhấp chuột nào vào .backdrop
phần tử sẽ được coi là "bên ngoài .target
phần tử".
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
Bạn có thể sử dụng ref
s để đạt được điều này, một cái gì đó như sau sẽ hoạt động.
Thêm vào ref
phần tử của bạn:
<div ref={(element) => { this.myElement = element; }}></div>
Sau đó, bạn có thể thêm một hàm để xử lý nhấp chuột bên ngoài phần tử như sau:
handleClickOutside(e) {
if (!this.myElement.contains(e)) {
this.setState({ myElementVisibility: false });
}
}
Sau đó, cuối cùng, thêm và xóa các trình nghe sự kiện trên sẽ gắn kết và sẽ ngắt kết nối.
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
vào document.addEventListener()
bằng cách thêm this
tham chiếu. Nếu không nó mang lại cho ReferenceError chưa gặp: handleClickOutside không được định nghĩa trongcomponentWillMount()
Đến bữa tiệc rất muộn, nhưng tôi đã thành công với việc thiết lập một sự kiện mờ trên phần tử chính của menu thả xuống với mã được liên kết để đóng menu thả xuống và cũng đính kèm trình nghe mousedown vào phần tử mẹ để kiểm tra xem menu thả xuống có mở không hoặc không, và sẽ dừng truyền sự kiện nếu nó đang mở để sự kiện mờ không được kích hoạt.
Vì sự kiện chuyển động xuống bong bóng nên điều này sẽ ngăn không cho bất kỳ thao tác chuyển xuống nào đối với trẻ em gây mờ cho phụ huynh.
/* 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 () không cần phải được gọi như tôi có thể giải thích nhưng firefox không hoạt động tốt nếu không có lý do gì. Hoạt động trên Chrome, Firefox và Safari.
Tôi đã tìm thấy một cách đơn giản hơn về điều này.
Bạn chỉ cần thêm onHide(this.closeFunction)
vào phương thức
<Modal onHide={this.closeFunction}>
...
</Modal>
Giả sử bạn có một chức năng để đóng phương thức.
Sử dụng mixin phản ứng bên ngoài tuyệt vời :
npm install --save react-onclickoutside
Và sau đó
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});