Next.js应用中作为子组件传入的div的onMouseEnter事件失效
问题:Google Maps AdvancedMarkerView 中 React 组件的鼠标事件无法触发
我开发了一款基于Google Maps的地图应用,用自定义div作为标记,通过MyMarker组件渲染到地图上,实现方案参考自Map Developers官方YouTube频道。现在需要给标记div添加onClick和onMouseEnter事件,但onMouseEnter始终无法触发,直接渲染div也无法解决问题。
核心代码
Properties 组件
const Properties = ({ data, map }) => { const [highlited, sethighlited] = useState(null) function handleMouseEnter() { console.log('Mouse entered'); } return ( <> { data.map((e, i) => { return (<MyMarker key={i} map={map} data={e} position={{ lat: e.location.coords.latitude, lng: e.location.coords.longitude, }}> <div className={` bg-blue-700 text-white font-bold py-2 px-4 rounded-full`} onMouseEnter={() => console.log("Mouse enter")}> <h2>{`${e.price}`}</h2> </div> </MyMarker>)}) } </> ) }
MyMarker 组件
const MyMarker = ({ data, map, children, position }) => { const rootRef = useRef() const markerRef = useRef() useEffect(() => { if (!rootRef.current) { const container = document.createElement("div"); rootRef.current = createRoot(container) markerRef.current = new google.maps.marker.AdvancedMarkerView({ position: position, content: container }) } }, []) useEffect(() => { rootRef.current.render(children) markerRef.current.position = position markerRef.current.map = map }, [position, map, children]) }
解决方案
原因分析
React的合成事件(如onMouseEnter)依赖事件委托机制,需要事件冒泡到React根节点才能被捕获。但MyMarker中用于渲染children的div,是挂载到Google Maps创建的独立DOM容器中,这个容器不在React根节点的DOM树范围内,导致合成事件无法正常触发。
方案一:使用Google Maps原生事件监听
直接在AdvancedMarkerView实例上绑定Google Maps提供的原生事件,更贴合地图组件的事件机制:
修改MyMarker组件,新增事件监听逻辑:
const MyMarker = ({ data, map, children, position, onMouseEnter, onClick }) => { const rootRef = useRef() const markerRef = useRef() useEffect(() => { if (!rootRef.current) { const container = document.createElement("div"); rootRef.current = createRoot(container) markerRef.current = new google.maps.marker.AdvancedMarkerView({ position: position, content: container, clickable: true // 显式标记为可点击 }) // 绑定Google Maps原生事件 if (onMouseEnter) { google.maps.event.addListener(markerRef.current, 'mouseover', onMouseEnter); } if (onClick) { google.maps.event.addListener(markerRef.current, 'click', onClick); } } // 组件卸载时清理事件监听 return () => { if (markerRef.current) { google.maps.event.clearInstanceListeners(markerRef.current); } } }, [onMouseEnter, onClick]) useEffect(() => { rootRef.current.render(children) markerRef.current.position = position markerRef.current.map = map }, [position, map, children]) return null }
在Properties组件中传递事件回调:
<MyMarker key={i} map={map} data={e} position={{ lat: e.location.coords.latitude, lng: e.location.coords.longitude, }} onMouseEnter={() => console.log("Mouse enter")} onClick={() => console.log("Marker clicked")} > <div className={` bg-blue-700 text-white font-bold py-2 px-4 rounded-full`}> <h2>{`${e.price}`}</h2> </div> </MyMarker>
方案二:使用原生DOM事件绑定
给标记div添加原生事件监听,同时确保元素可以接收鼠标事件:
修改Properties组件中的标记div:
const Properties = ({ data, map }) => { const [highlited, sethighlited] = useState(null) return ( <> {data.map((e, i) => { const markerDivRef = useRef(null); useEffect(() => { const handleMouseEnter = () => console.log("Mouse enter"); const handleClick = () => console.log("Marker clicked"); if (markerDivRef.current) { markerDivRef.current.addEventListener('mouseenter', handleMouseEnter); markerDivRef.current.addEventListener('click', handleClick); // 清理事件 return () => { markerDivRef.current.removeEventListener('mouseenter', handleMouseEnter); markerDivRef.current.removeEventListener('click', handleClick); } } }, []); return ( <MyMarker key={i} map={map} data={e} position={{ lat: e.location.coords.latitude, lng: e.location.coords.longitude, }}> <div ref={markerDivRef} className={` bg-blue-700 text-white font-bold py-2 px-4 rounded-full`} style={{ pointerEvents: 'auto' }} // 确保元素可接收鼠标事件 > <h2>{`${e.price}`}</h2> </div> </MyMarker> ) })} </> ) }
内容的提问来源于stack exchange,提问作者Muhammed Rashid
相关产品推荐
相关产品推荐

