检测 React 组件外的点击
检测 React 组件外的点击
我正在寻找一种方法来检测是否发生了组件外的点击事件,就像这个文章中描述的那样。使用jQuery closest()来查看点击事件的目标是否有dom元素作为其父元素。如果匹配,则点击事件属于其中一个子级,因此不被认为是在组件外发生的。
因此,在我的组件中,我想将点击处理程序附加到window
上。当处理程序触发时,我需要将目标与我的组件的dom子级进行比较。
点击事件包含类似于\"path\"的属性,它似乎保存了事件所经过的dom路径。我不确定要比较什么或以什么方式遍历它,并且我认为某人一定已经将其放在一个巧妙的实用函数中了...没有吗?
admin 更改状态以发布 2023年5月23日
我也遇到了同样的问题。虽然我来晚了,但对我来说,这是一个非常好的解决方案。希望它能对其他人有所帮助。你需要从react-dom
中导入findDOMNode
。
import ReactDOM from 'react-dom'; // ... componentDidMount() { document.addEventListener('click', this.handleClickOutside, true); } componentWillUnmount() { document.removeEventListener('click', this.handleClickOutside, true); } handleClickOutside = event => { const domNode = ReactDOM.findDOMNode(this); if (!domNode || !domNode.contains(event.target)) { this.setState({ visible: false }); } }
React钩子方法(16.8+)
你可以创建一个可重用的钩子方法,叫做useComponentVisible
。
import { useState, useEffect, useRef } from 'react'; export default function useComponentVisible(initialIsVisible) { const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible); const ref = useRef(null); const handleClickOutside = (event) => { if (ref.current && !ref.current.contains(event.target)) { setIsComponentVisible(false); } }; useEffect(() => { document.addEventListener('click', handleClickOutside, true); return () => { document.removeEventListener('click', handleClickOutside, true); }; }, []); return { ref, isComponentVisible, setIsComponentVisible }; }
然后在你希望添加功能的组件中执行以下操作:
const DropDown = () => { const { ref, isComponentVisible } = useComponentVisible(true); return ( {isComponentVisible && (Dropdown Component
)} ); }
在此处查看codesandbox示例。
下文解决方案使用ES6,遵循绑定和方法设置引用的最佳实践。
查看实际应用:
Hooks实现:
import React, { useRef, useEffect } from "react"; /** * Hook that alerts clicks outside of the passed ref */ function useOutsideAlerter(ref) { useEffect(() => { /** * Alert if clicked on outside of element */ function handleClickOutside(event) { if (ref.current && !ref.current.contains(event.target)) { alert("You clicked outside of me!"); } } // Bind the event listener document.addEventListener("mousedown", handleClickOutside); return () => { // Unbind the event listener on clean up document.removeEventListener("mousedown", handleClickOutside); }; }, [ref]); } /** * Component that alerts if you click outside of it */ export default function OutsideAlerter(props) { const wrapperRef = useRef(null); useOutsideAlerter(wrapperRef); return {props.children}; }
类实现:
16.3之后
import React, { Component } from "react"; /** * Component that alerts if you click outside of it */ export default class OutsideAlerter extends Component { constructor(props) { super(props); this.wrapperRef = React.createRef(); this.handleClickOutside = this.handleClickOutside.bind(this); } componentDidMount() { document.addEventListener("mousedown", this.handleClickOutside); } componentWillUnmount() { document.removeEventListener("mousedown", this.handleClickOutside); } /** * Alert if clicked on outside of element */ handleClickOutside(event) { if (this.wrapperRef && !this.wrapperRef.current.contains(event.target)) { alert("You clicked outside of me!"); } } render() { return {this.props.children}; } }
16.3之前
import React, { Component } from "react"; /** * Component that alerts if you click outside of it */ export default class OutsideAlerter extends Component { constructor(props) { super(props); this.setWrapperRef = this.setWrapperRef.bind(this); this.handleClickOutside = this.handleClickOutside.bind(this); } componentDidMount() { document.addEventListener("mousedown", this.handleClickOutside); } componentWillUnmount() { document.removeEventListener("mousedown", this.handleClickOutside); } /** * Set the wrapper ref */ setWrapperRef(node) { this.wrapperRef = node; } /** * Alert if clicked on outside of element */ handleClickOutside(event) { if (this.wrapperRef && !this.wrapperRef.contains(event.target)) { alert("You clicked outside of me!"); } } render() { return {this.props.children}; } }