React 在元素外部切换
React 在元素外部切换
我正在寻找一种检测单击事件是否发生在组件外部的方法,就像这篇文章中描述的那样。使用jQuery closest()来查看点击事件的目标是否有dom元素作为其父元素之一。如果存在匹配项,则单击事件属于其中一个子元素,因此不被视为组件外部。
在我的组件中,我想将一个点击处理程序附加到window
。当处理程序触发时,我需要将目标与我的组件的dom子元素进行比较。
单击事件包含像“path”这样的属性,似乎保存了事件已经遍历的dom路径。我不确定要比较什么或如何最好地遍历它,我想有人肯定已经在一个聪明的实用程序函数中完成了这个,对不对?
admin 更改状态以发布 2023年5月24日
我曾被同样的问题困住。虽然有些晚加入这里,但对我来说这是一个非常好的解决方案。希望能对其他人有所帮助。你需要从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 Hooks方法(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并遵循绑定和通过方法设置ref的最佳实践。
查看它的实际效果:
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}; } }