如何实现屏蔽除滚动外的所有鼠标事件?
解决加载遮罩屏蔽交互同时保留滚动的方案
核心思路
给遮罩设置pointer-events: none让滚动事件穿透到下方元素,同时通过window层面的事件捕获,判断点击/触摸等交互的目标是否处于遮罩覆盖范围内,若在范围内则阻止事件触发,实现「屏蔽交互+保留滚动」的效果。
具体实现
1. 遮罩基础样式
保持遮罩的定位、半透明和加载动画,关键设置pointer-events: none允许滚动穿透:
.loading-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255,255,255,0.7); display: flex; align-items: center; justify-content: center; pointer-events: none; /* 核心:允许滚动事件穿透 */ z-index: 999; } /* 可选:加载动画样式 */ .loading-spinner { width: 40px; height: 40px; border: 4px solid #ccc; border-top-color: #333; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { to { transform: rotate(360deg); } }
2. Window层面的事件拦截
通过事件捕获机制,在window上监听需要屏蔽的交互事件,判断目标元素是否被遮罩覆盖,是则阻止事件:
// 初始化遮罩(通用组件化写法) class LoadingOverlay { constructor() { this.overlay = this.createOverlay(); this.eventHandlers = []; this.bindBlockEvents(); } // 创建遮罩DOM createOverlay() { const overlay = document.createElement('div'); overlay.className = 'loading-overlay'; const spinner = document.createElement('div'); spinner.className = 'loading-spinner'; overlay.appendChild(spinner); return overlay; } // 显示遮罩,可指定目标容器 show(target = document.body) { target.style.position = 'relative'; // 确保遮罩相对目标定位 target.appendChild(this.overlay); } // 隐藏遮罩 hide() { this.overlay.remove(); } // 绑定需要拦截的事件 bindBlockEvents() { const blockEventTypes = ['click', 'mousedown', 'touchstart', 'keydown']; blockEventTypes.forEach(type => { const handler = (e) => { let target = e.target; // 向上遍历DOM,检查目标是否被遮罩覆盖 while (target && target !== document.body) { const targetRect = target.getBoundingClientRect(); const overlayRect = this.overlay.getBoundingClientRect(); // 判断元素是否在遮罩范围内 const isCovered = targetRect.top < overlayRect.bottom && targetRect.bottom > overlayRect.top && targetRect.left < overlayRect.right && targetRect.right > overlayRect.left; if (isCovered) { e.stopPropagation(); e.preventDefault(); return; } target = target.parentNode; } }; // 捕获阶段触发,确保先于元素自身事件处理 window.addEventListener(type, handler, true); this.eventHandlers.push({ type, handler }); }); } // 移除事件监听,避免内存泄漏 unbindBlockEvents() { this.eventHandlers.forEach(({ type, handler }) => { window.removeEventListener(type, handler, true); }); this.eventHandlers = []; } // 销毁组件 destroy() { this.hide(); this.unbindBlockEvents(); } }
3. 使用示例
// 创建遮罩实例 const loadingMask = new LoadingOverlay(); // 在指定容器上显示遮罩 const targetContainer = document.getElementById('scrollable-div'); loadingMask.show(targetContainer); // 加载完成后隐藏/销毁 // loadingMask.hide(); // loadingMask.destroy();
方案优势
- 完全适配需求:无需了解下方内容结构或滚动元素数量,遮罩直接放目标上方即可
- 改动极小:不需要修改原有业务代码,仅引入通用组件
- 基于window层面处理:通过事件捕获统一拦截交互,不侵入业务逻辑
内容的提问来源于stack exchange,提问作者tris
相关产品推荐
相关产品推荐

