
本文详细介绍了在cxjs应用中,如何解决默认onwheel事件的被动监听限制,从而成功阻止其默认行为。通过利用onref属性获取dom元素引用,并结合cx/util中的addeventlistenerwithoptions方法,我们可以灵活地添加非被动式(active)的wheel事件监听器,确保e.preventdefault()功能正常生效,实现对滚轮事件的精确控制。
理解CxJS中的滚轮事件限制
在CxJS应用中,以及其底层框架React中,为了优化滚动性能,诸如onWheel、onTouchStart等触摸和滚轮事件默认被浏览器注册为“被动(passive)”事件监听器。这意味着当这些事件触发时,浏览器不会等待事件处理函数执行完毕,而是会立即处理滚动。这种机制显著提升了页面的滚动流畅度,但也带来了一个重要的限制:在被动事件监听器中调用event.preventDefault()将无效,并且在开发模式下可能会抛出警告或错误,提示“Unable to preventDefault inside passive event listener”。
当我们的应用场景需要阻止页面的默认滚动行为(例如,实现自定义的缩放、平移或特定的滚动区域)时,默认的onWheel事件监听器就无法满足需求。为了实现对滚轮事件的完全控制并允许阻止其默认行为,我们需要显式地注册一个非被动式(active)的滚轮事件监听器。
实现非被动式滚轮事件监听
CxJS提供了一种灵活且健壮的方法来解决这一限制,即结合使用组件的onRef属性和cx/util模块中的addEventListenerWithOptions方法。
1. 获取目标DOM元素的引用
由于我们无法直接通过JSX属性(如
{/* 此div内的内容 */}
在这里,addElementListener将是我们在CxJS组件类中定义的一个方法,它会在div元素被创建并挂载到DOM时被调用。
2. 使用addEventListenerWithOptions添加非被动式监听器
cx/util模块提供了一个名为addEventListenerWithOptions的实用函数,它封装了原生Element.prototype.addEventListener,并允许我们方便地传递事件选项,包括passive状态。
首先,您需要在组件文件中导入这个函数:
import { addEventListenerWithOptions } from 'cx/util';接下来,在您的CxJS组件类中定义addElementListener方法。这个方法将负责在接收到DOM元素时,添加我们的非被动式滚轮事件监听器,并确保其生命周期得到妥善管理。
// 假设这是您的CxJS组件类内部
class MyComponent extends Widget {
// 用于存储取消订阅函数的变量,以便在组件更新或卸载时清理旧的监听器
unsubscribeScroll: () => void;
/**
* onRef回调函数,当目标DOM元素可用时被调用。
* @param {Element} el - 目标DOM元素。
*/
addElementListener(el: Element) {
// 在添加新的监听器之前,如果存在旧的监听器,先将其移除。
// 这对于处理组件更新或重新渲染的情况至关重要,防止重复添加和内存泄漏。
if (this.unsubscribeScroll) {
this.unsubscribeScroll();
}
// 如果元素不存在(例如,组件正在卸载),则直接返回
if (!el) {
return;
}
// 使用addEventListenerWithOptions添加非被动式滚轮事件监听器
this.unsubscribeScroll = addEventListenerWithOptions(
el, // 第一个参数:目标DOM元素
'wheel', // 第二个参数:事件类型,注意是原生DOM事件名称 'wheel'
(e: WheelEvent) => { // 第三个参数:事件处理函数
e.preventDefault(); // 在这里,preventDefault()将正常工作,阻止默认滚动
// 在此处添加您的自定义滚轮逻辑,例如:
// console.log('滚轮事件触发,阻止了默认滚动。DeltaY:', e.deltaY);
// 根据e.deltaY的值实现自定义缩放、滚动或其他交互
},
{ passive: false } // 第四个参数:事件选项,关键是设置 passive: false
);
}
/**
* 组件生命周期方法:在组件即将被销毁时调用。
* 确保在此处移除事件监听器,以防止内存泄漏。
*/
onDestroy() {
if (this.unsubscribeScroll) {
this.unsubscribeScroll();
}
}
}在上述代码中:
- 我们声明了一个unsubscribeScroll变量来存储addEventListenerWithOptions返回的取消订阅函数。这个函数在被调用时会移除对应的事件监听器,对于管理事件监听器的生命周期至关重要。
- 在addElementListener方法内部,我们首先检查并移除任何可能存在的旧监听器,以妥善处理组件的更新和重新渲染。
- addEventListenerWithOptions的第三个参数是我们的事件处理函数,其中我们可以安全地调用e.preventDefault()。
- 最关键的是第四个参数 { passive: false },它明确告诉浏览器这是一个非被动式监听器,允许我们阻止默认行为。
- onDestroy生命周期方法用于确保当组件被销毁时,事件监听器也会被正确移除,避免潜在的内存泄漏。
完整示例
将上述代码片段整合到一个完整的CxJS组件中,即可实现一个带有非被动式滚轮事件监听器的div。
import { Widget, VDOM } from 'cx/ui';
import { addEventListenerWithOptions } from 'cx/util';
// 定义一个CxJS组件,用于演示非被动式滚轮事件
class CustomScrollDiv extends Widget {
// 用于存储取消订阅函数的变量
unsubscribeScroll: () => void;
/**
* onRef回调函数,当目标DOM元素可用时被调用。
* @param {Element} el - 目标DOM元素。
*/
addElementListener(el: Element) {
// 清理旧的监听器,防止重复添加和内存泄漏
if (this.unsubscribeScroll) {
this.unsubscribeScroll();
}
// 如果元素不存在(例如,组件正在卸载),则直接返回
if (!el) {
return;
}
// 添加非被动式滚轮事件监听器
this.unsubscribeScroll = addEventListenerWithOptions(
el,
'wheel',
(e: WheelEvent) => {
e.preventDefault(); // 阻止默认的页面滚动行为
console.log('自定义滚轮事件触发,阻止了默认滚动。DeltaY:', e.deltaY);
// 在这里添加您的自定义逻辑,例如:
// if (e.deltaY < 0) {
// // 向上滚动逻辑
// } else {
// // 向下滚动逻辑
// }
},
{ passive: false } // 关键选项:将事件设置为非被动式
);
}
/**
* 组件生命周期方法:在组件即将被销毁时调用。
* 确保在此处移除事件监听器,以防止内存泄漏。
*/
onDestroy() {
if (this.unsubscribeScroll) {
this.unsubscribeScroll();
}
}
render() {
// 渲染一个带有onRef属性的div
return (
请在此绿色边框区域内滚动鼠标滚轮。您会发现页面的默认滚动行为被阻止,同时控制台会输出滚轮事件信息。
内容1
内容2
内容3
内容4
内容5
内容6
内容7
内容8
内容9
内容10
内容11
内容12
内容13
内容14
内容15
);
}
}
// 您可以在您的CxJS应用中像这样使用这个组件:
// 注意事项与最佳实践
- 清理监听器是关键: 始终确保在组件卸载时,或者在onRef回调因组件重新渲染而再次被调用时,移除旧的事件监听器。addEventListenerWithOptions返回的函数就是为此目的而设计。不清理监听器会导致内存泄漏和潜在的意外行为。
- 事件类型: 在addEventListenerWithOptions中,您应该使用原生DOM事件名称(例如'wheel'),而不是JSX属性名称(例如'onWheel')。
- 性能考量: 默认将事件设置为被动是为了优化性能。只有当您确实需要阻止默认行为时,才应使用{ passive: false }。过度使用非被动事件可能会对页面的滚动性能产生负面影响,因为它强制浏览器等待您的事件处理函数执行完毕,才能进行滚动。
- 浏览器兼容性: 现代浏览器普遍支持passive选项。对于一些旧版浏览器,passive选项可能会被忽略,preventDefault()会正常工作(但可能导致滚动卡顿)。CxJS的addEventListenerWithOptions已经考虑了这些兼容性。
总结
通过巧妙地利用CxJS的onRef属性来获取DOM元素引用,并结合cx/util模块中强大的addEventListenerWithOptions方法,我们可以有效地解决onWheel事件的被动监听限制。这种方法赋予了开发者在需要时精确控制滚









