浏览器控制台输出:[Violation] Added non-passive event listener to a scroll-blocking <某些> 事件. Consider marking event handler as 'passive' to make the page more responsive. See <URL>
弃案:刚开始使用的是插件default-passive-events,但是发现使用了之后某些UI库的组件会有问题。例如 ng-zorro-antd/date-picker选择时间后,会报错 然后输入框不会出现选择的日期。故放弃。
第一步,找出是哪个组件/类发出的警告
在浏览器控制台输入:
(function() {
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
if (type === 'touchstart' || type === 'touchmove' || type === 'wheel' || type === 'mousewheel') {
// 获取更详细的元素信息
const elementInfo = {
target:this,
// 元素自己的信息
self: {
tagName: this.tagName,
id: this.id,
className: this.className,
classList: Array.from(this.classList || []), // 打印所有类名
attributes: Array.from(this.attributes || []).map(attr => ({
name: attr.name,
value: attr.value
}))
},
// 父元素信息
parentElement: this.parentElement ? {
tagName: this.parentElement.tagName,
id: this.parentElement.id,
className: this.parentElement.className,
classList: Array.from(this.parentElement.classList || [])
} : null,
// 所有父元素的类名
parentClasses: (function(element) {
const classes = [];
let current = element;
while (current && current.parentElement) {
current = current.parentElement;
if (current.className) {
classes.push({
tagName: current.tagName,
id: current.id,
className: current.className,
classList: Array.from(current.classList || [])
});
}
}
return classes;
})(this)
};
console.warn('Event Listener Added:', {
target:this,
eventType: type,
elementInfo,
options,
stack: new Error().stack,
timestamp: new Date().toISOString()
});
}
return originalAddEventListener.apply(this, arguments);
};
console.log('Event listener monitor installed');
})();
然后回打印发出警告的元素详情信息
第二步 隐藏这个提示。给发出提示的组件相关事件添加 { passive: true }
然后根据需要在入口文件或者指定组件中执行下面代码:
handleScrollBlockingWarning(){
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
// 需要处理的事件类型
const passiveEvents = ['touchstart', 'touchmove', 'wheel', 'mousewheel'];
if (passiveEvents.includes(type)) {
const isAntTabsNavList = this instanceof HTMLElement &&
this.classList.contains('ant-tabs-nav-list');
if (isAntTabsNavList) {
// 对于 ant-tabs-nav-list,强制使用 passive: true
options = typeof options === 'boolean'
? { passive: true, capture: options }
: { ...(options || {}), passive: true };
}
}
return originalAddEventListener.call(this, type, listener, options);
};
}
设置后,当组件触发这些事件时['touchstart', 'touchmove', 'wheel', 'mousewheel'],控制台会报错
Unable to preventDefault inside passive event listener
报错原因是在设置了passive: true 的监听事件中。调用了 e.preventDefault();
例如:
element.addEventListener('touchmove', function(e) {
e.preventDefault();
}, { passive: true });
第三步,将报错的事件中 e.preventDefault 置为空方法
然后在拦截事件时把它的e.preventDefault置为空方法。
e.preventDefault = function () {};
handleScrollBlockingWarning() {
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function (type, listener, options) {
// 需要处理的事件类型
const passiveEvents = ['touchstart', 'touchmove', 'wheel', 'mousewheel'];
if (passiveEvents.includes(type)) {
const isBarChartDiv = this instanceof HTMLElement && this.tagName === 'DIV' &&
this.parentElement &&
this.parentElement.classList.contains('echart-div');
const isAntTabsNavList = this instanceof HTMLElement &&
this.classList.contains('ant-tabs-nav-list');
if (isAntTabsNavList || isBarChartDiv) {
const originalListener = listener;
listener = function (this: EventTarget, e) {
// 替换 preventDefault
const originalPreventDefault = e.preventDefault;
e.preventDefault = function () {
// 什么都不做
// 你也可以加日志
// console.warn('preventDefault 被拦截');
};
try {
if (typeof originalListener === 'function') {
// 是函数,直接调用
originalListener.call(this, e);
} else if (originalListener && typeof originalListener.handleEvent === 'function') {
// 是对象,调用 handleEvent
originalListener.handleEvent.call(originalListener, e);
}
} finally {
// 恢复原方法,避免影响其他事件
e.preventDefault = originalPreventDefault;
}
};
}
if (isAntTabsNavList) {
// console.warn("handleScrollBlockingWarning---", listener);
// 对于 ant-tabs-nav-list,强制使用 passive: true
options = typeof options === 'boolean'
? { passive: true, capture: options }
: { ...(options || {}), passive: true };
} else if (isBarChartDiv) {
// console.warn("handleScrollBlockingWarning---", listener);
options = typeof options === 'boolean'
? { passive: true, capture: options }
: { ...(options || {}), passive: true };
}
}
return originalAddEventListener.call(this, type, listener, options);
};
}
这样就可以解决
但是这样处理的组件,如果需要用到wheel手势。可能会出现手势冲突。 例如:这个组件的父组件也要响应wheel事件,那么上面的代码就会导致。两个组件同时响应wheel事件。
感觉有点得不偿失。因为要面临组件一些未知的手势冲突,可根据实际情况决定是否使用该方法