React中点击拖拽编辑数值的事件冲突问题求助
事件冲突解决与拖拽编辑实现方案
问题根源
- 每个
Location组件都给window绑定了mousedown事件,导致点击页面时所有组件的回调都会触发,无法区分目标组件。 - 依赖
window事件的offsetY属性判断点击区域,该值是相对于视口而非组件本身,坐标逻辑完全错误。 e.stopPropagation()在window事件中无效,因为事件已经冒泡到全局层级。
解决方案
1. 将事件绑定到组件自身而非全局
直接在组件的根div上使用React的onMouseDown属性绑定事件,只有点击当前组件才会触发对应的回调,彻底避免全局监听的冲突。
修改组件返回部分:
return ( <div className="location" ref={divRef} onMouseDown={handleMouseDown} > <div className="location-item">{props.bars}.</div> <div className="location-item">{props.beats}.</div> <div className="location-item">{props.divisions}</div> </div> )
同时删除useEffect中全局的mousedown绑定:
useEffect(() => { console.log(`init`) // 移除全局mousedown绑定 return () => { console.log(`shutdown`) window.removeEventListener('mouseup', handleMouseUp) window.removeEventListener('mousemove', handleMouseMove) } }, [])
2. 修正坐标计算逻辑
组件自身的mousedown事件中,e.offsetY是相对于当前组件的,可直接用于判断点击区域。但mousemove事件绑定在window上,需要通过组件的位置计算相对坐标:
修改handleMouseMove函数:
const handleMouseMove = (e: any) => { if (!divRef.current) return; // 获取组件在视口中的位置 const rect = divRef.current.getBoundingClientRect(); // 计算鼠标相对于组件顶部的Y坐标 const currentMouseY = e.clientY - rect.top; const distanceY = lastMouseYRef.current - currentMouseY; console.log(`location.handleMouseMove(${props.id}): drag distanceY ${distanceY}`) if (Math.abs(distanceY) > divRef.current.clientHeight) { console.log(`lastMouseYRef.current ${lastMouseYRef.current} - currentMouseY ${currentMouseY} > divRef.current.clientHeight ${divRef.current.clientHeight} `) const newLastMouseY = currentMouseY; setLastMouseY(newLastMouseY) lastMouseYRef.current = newLastMouseY; const offset: number = distanceY > 0 ? 1 : -1 console.log(`bink!! distanceY ${distanceY} dragItemIndex ${dragItemIndexRef.current} offset ${offset}`) props.onEdit(dragItemIndexRef.current, offset) } }
3. 补充禁用状态判断
在handleMouseDown开头添加enabled状态判断,禁用时不响应拖拽:
const handleMouseDown = (e: any) => { if (!props.enabled || !divRef.current) { return } // 原有逻辑... }
完整修改后的组件代码
import React, {useEffect, useState, useRef} from "react" import "./location.css" export const Location = (props: any) => { const [mouseDown, setMouseDown] = useState<boolean>(false) const [lastMouseY, setLastMouseY] = useState<number>(0) const mouseDownRef = useRef(mouseDown) const lastMouseYRef = useRef(lastMouseY) const [dragItemIndex, setDragItemIndex] = useState<number>(0) const dragItemIndexRef = useRef(dragItemIndex) const divRef = useRef<HTMLDivElement>(null); const handleMouseDown = (e: any) => { if (!props.enabled || !divRef.current) { return } console.log(`location.handleMouseDown(${props.id}): e.offsetY ${e.offsetY}`) if (e.offsetY >= 0 && e.offsetY <= divRef.current.clientHeight) { console.log(`start ${props.id} e.offsetY ${e.offsetY}`) const initialMouseY = e.offsetY; setLastMouseY(initialMouseY) lastMouseYRef.current = initialMouseY; window.addEventListener('mouseup', handleMouseUp) window.addEventListener('mousemove', handleMouseMove) setMouseDown(true) let dragItemIndex = 1 const clientLeft = divRef.current.getBoundingClientRect().left const offsetX = e.clientX - clientLeft if (offsetX < divRef.current.clientWidth / 3) { dragItemIndex = 0 } else if (offsetX > (divRef.current.clientWidth * 2 / 3)) { dragItemIndex = 2 } console.log(`mouse down - dragItemIndex ${dragItemIndex} e.offsetY ${e.offsetY}`) setDragItemIndex(dragItemIndex) } } const handleMouseUp = (e: any) => { setMouseDown(false) window.removeEventListener('mouseup', handleMouseUp) window.removeEventListener('mousemove', handleMouseMove) } const handleMouseMove = (e: any) => { if (!divRef.current) return; const rect = divRef.current.getBoundingClientRect(); const currentMouseY = e.clientY - rect.top; const distanceY = lastMouseYRef.current - currentMouseY; console.log(`location.handleMouseMove(${props.id}): drag distanceY ${distanceY}`) if (Math.abs(distanceY) > divRef.current.clientHeight) { console.log(`lastMouseYRef.current ${lastMouseYRef.current} - currentMouseY ${currentMouseY} > divRef.current.clientHeight ${divRef.current.clientHeight} `) const newLastMouseY = currentMouseY; setLastMouseY(newLastMouseY) lastMouseYRef.current = newLastMouseY; const offset: number = distanceY > 0 ? 1 : -1 console.log(`bink!! distanceY ${distanceY} dragItemIndex ${dragItemIndexRef.current} offset ${offset}`) props.onEdit(dragItemIndexRef.current, offset) } } useEffect(() => { mouseDownRef.current = mouseDown }, [mouseDown]) useEffect(() => { lastMouseYRef.current = lastMouseY }, [lastMouseY]) useEffect(() => { dragItemIndexRef.current = dragItemIndex }, [dragItemIndex]) useEffect(() => { console.log(`init`) return () => { console.log(`shutdown`) window.removeEventListener('mouseup', handleMouseUp) window.removeEventListener('mousemove', handleMouseMove) } }, []) return ( <div className="location" ref={divRef} onMouseDown={handleMouseDown} > <div className="location-item">{props.bars}.</div> <div className="location-item">{props.beats}.</div> <div className="location-item">{props.divisions}</div> </div> ) }
内容的提问来源于stack exchange,提问作者ohthepain
相关产品推荐
相关产品推荐

