Rxjs中鼠标事件currentTarget丢失问题及改写需求
解决RxJS中事件currentTarget丢失的问题
嘿,我来帮你搞定这个困扰!你遇到的currentTarget丢失问题其实是浏览器事件机制和RxJS异步流结合导致的常见坑,咱们一个个来解决。
第一个问题:能否避免该丢弃现象?
当然可以!问题的根源在于:浏览器会在事件处理完成后复用或清理事件对象的属性,尤其是currentTarget——它只在事件触发的同步阶段指向绑定元素,等到RxJS的异步流(比如concatMap里的mousemove流)执行时,原始事件对象的currentTarget已经被重置了。
解决思路很简单:在事件触发的第一时间,把需要的currentTarget(或者其他关键属性)提取出来保存,不要等到后续流里再去访问事件对象的属性。
修改后的代码示例:
// 从事件中立即提取并保存currentTarget和原始事件 const canvasMousedown$ = Rx.Observable.fromEvent(chart, 'mousedown').map(event => ({ originalEvent: event, currentTarget: event.currentTarget as Element // 明确类型,避免TS报错 })); const canvasMousemove$ = Rx.Observable.fromEvent(chart, 'mousemove').map(event => ({ originalEvent: event, currentTarget: event.currentTarget as Element })); const canvasMouseup$ = Rx.Observable.fromEvent(chart, 'mouseup'); const canvasSelection$ = canvasMousedown$.concatMap((startData) => { return canvasMousemove$.takeUntil(canvasMouseup$).map((endData) => [ { ...startData.originalEvent, currentTarget: startData.currentTarget }, { ...endData.originalEvent, currentTarget: endData.currentTarget } ]); }); canvasSelection$.subscribe((events) => { // 现在currentTarget已经被正确保留了 console.log(events); // 直接传给第三方函数即可 thirdPartyFunction(events[0], events[1]); });
这样一来,我们在事件触发的同步阶段就把currentTarget的引用存下来了,后续异步流里用的是我们保存的引用,自然不会丢失。
第二个问题:能否改写或克隆事件以包含新的currentTarget?
因为原生事件对象的currentTarget是只读属性(TypeScript也会严格限制赋值),直接修改肯定不行,但我们可以通过包装事件对象或者克隆模拟事件的方式来实现。
方案1:创建事件包装对象(推荐)
这种方式最简单,不需要严格克隆原生Event实例,只需要把第三方函数需要的属性(包括自定义的currentTarget)打包成一个对象即可:
// 封装一个工具函数,生成包含自定义currentTarget的事件包装器 function createWrappedEvent(event: Event, target: Element) { // 复制原始事件的所有可枚举属性,再覆盖currentTarget return { ...event, currentTarget: target, // 如果第三方函数需要其他不可枚举属性,可以手动添加,比如: clientX: event.clientX, clientY: event.clientY } as unknown as Event; // 类型断言,让TS认为这是一个Event对象 } // 在流中使用这个函数 const canvasMousedown$ = Rx.Observable.fromEvent(chart, 'mousedown').map(event => createWrappedEvent(event, chart) // 这里直接把chart作为currentTarget,也可以换成你需要的元素 ); const canvasMousemove$ = Rx.Observable.fromEvent(chart, 'mousemove').map(event => createWrappedEvent(event, chart) );
方案2:克隆原生事件(适合严格需要Event实例的场景)
如果第三方函数必须接收标准的MouseEvent实例,我们可以用new MouseEvent()来克隆事件,再通过包装的方式注入自定义currentTarget:
function cloneMouseEventWithTarget(originalEvent: MouseEvent, newTarget: Element) { // 克隆事件的核心属性 const clonedEvent = new MouseEvent(originalEvent.type, { bubbles: originalEvent.bubbles, cancelable: originalEvent.cancelable, clientX: originalEvent.clientX, clientY: originalEvent.clientY, screenX: originalEvent.screenX, screenY: originalEvent.screenY, // 按需添加其他需要的属性 }); // 因为currentTarget只读,所以用Object.assign包装一层 return Object.assign(clonedEvent, { currentTarget: newTarget }) as unknown as MouseEvent; }
这种方式更接近原生事件,但需要手动复制所有需要的属性,相对繁琐一些,所以优先推荐方案1。
内容的提问来源于stack exchange,提问作者Máté Balogh
相关产品推荐
相关产品推荐

