本文详解如何通过 css pointer-events 分层控制,构建一个仅锁定页面主体、却始终保留钥匙按钮可点击的 tampermonkey 屏幕锁工具,并解决 z-index 冲突、视频播放中断等常见问题。
本文详解如何通过 css pointer-events 分层控制,构建一个仅锁定页面主体、却始终保留钥匙按钮可点击的 tampermonkey 屏幕锁工具,并解决 z-index 冲突、视频播放中断等常见问题。
在开发浏览器端临时防误触工具时,一个直观需求是:用一个固定位置的“钥匙”图标(如圆形按钮)作为开关,点击即全局锁定页面交互——但钥匙自身必须始终保持可点击,否则一旦锁定便无法解锁。原始方案中直接对 document.body 设置 pointer-events: none 会导致子元素(包括钥匙)一并失效,这是 CSS 继承行为的典型表现:pointer-events 虽不完全继承,但父级设为 none 时,子元素默认无法响应事件,除非显式重置。
核心解法在于分层覆盖策略:在锁定状态下,先禁用整个页面的指针事件,再对钥匙元素单独启用。只需在 lockButton.style.pointerEvents = 'auto' 这一行补全即可突破限制。以下是优化后的完整脚本:
// ==UserScript==
// @name Screen Lock Key
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 精确锁定页面交互,钥匙按钮始终可点击|支持视频持续播放
// @author You
// @match *://*/*
// @grant none
// ==/UserScript==
(function() {
'use strict';
// 创建钥匙按钮(使用 SVG 图标提升语义性与缩放兼容性)
const lockButton = document.createElement('div');
lockButton.id = 'tm-lock-key';
lockButton.innerHTML = `
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
<path d="M12 3a4 4 0 0 0-4 4v1H6a2 2 0 0 0 0 4h1v1a4 4 0 0 0 8 0v-1h1a2 2 0 0 0 0-4h-1V7a4 4 0 0 0-4-4z"/>
</svg>
`;
Object.assign(lockButton.style, {
position: 'fixed',
left: '8px',
top: '50%',
transform: 'translateY(-50%)',
width: '32px',
height: '32px',
backgroundColor: '#666',
borderRadius: '50%',
cursor: 'pointer',
zIndex: '2147483647', // 使用最大安全整数,避免被页面 z-index 覆盖
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
transition: 'all 0.2s ease'
});
// 添加悬停反馈
lockButton.addEventListener('mouseenter', () => {
if (isLocked) lockButton.style.backgroundColor = '#4CAF50';
});
lockButton.addEventListener('mouseleave', () => {
if (isLocked) lockButton.style.backgroundColor = '#666';
});
document.body.appendChild(lockButton);
let isLocked = false;
lockButton.addEventListener('click', function(e) {
e.stopPropagation(); // 阻止事件冒泡,确保 body 不意外捕获
if (isLocked) {
isLocked = false;
lockButton.style.backgroundColor = '#666';
lockButton.style.transform = 'translateY(-50%) scale(1)';
document.body.style.pointerEvents = 'auto';
// 可选:恢复页面滚动能力(若之前禁用过)
document.body.style.overflow = 'visible';
} else {
isLocked = true;
lockButton.style.backgroundColor = '#4CAF50';
lockButton.style.transform = 'translateY(-50%) scale(1.1)';
document.body.style.pointerEvents = 'none';
lockButton.style.pointerEvents = 'auto'; // ✅ 关键修复:显式启用钥匙自身事件
}
});
// 【重要】防止页面 JS 动态插入高 z-index 元素遮挡钥匙
// 启用 MutationObserver 实时监控并修正层级
const observer = new MutationObserver(() => {
const highestZ = Math.max(
...Array.from(document.querySelectorAll('*'))
.map(el => parseInt(window.getComputedStyle(el).zIndex) || 0),
0
);
if (highestZ >= 2147483647) {
lockButton.style.zIndex = String(highestZ + 1);
}
});
observer.observe(document.body, { childList: true, subtree: true });
// 【补充】确保视频/音频播放不受影响(pointer-events 不影响媒体播放)
// 如需进一步保障,可添加:document.body.style['webkitTapHighlightColor'] = 'transparent';
})();✅ 关键优化说明:
- 精准事件隔离:lockButton.style.pointerEvents = 'auto' 是解决“钥匙也被锁”的根本方案,必须在 body.pointerEvents = 'none' 后立即执行;
- 抗遮挡设计:采用 zIndex: 2147483647(CSS 最大安全整数),并配合 MutationObserver 动态监测页面 z-index 变化,自动提升钥匙层级;
- 用户体验增强:添加 SVG 图标、悬停变色、平滑缩放动画及阴影,提升视觉反馈;
- 媒体兼容性:pointer-events: none 仅影响鼠标/触摸交互,不影响 <video> <audio> 的自动播放、音量控制或进度条拖拽(这些由浏览器原生控件处理,且通常位于 shadow DOM 中,不受外部 pointer-events 影响);
- 防误触发:e.stopPropagation() 避免点击钥匙时意外触发 body 上可能存在的其他监听器。
⚠️ 注意事项:
- 若目标网站使用 iframe 嵌入内容(如 YouTube 视频),该脚本无法锁定 iframe 内部交互(受同源策略限制),但 iframe 外部页面仍会被正常锁定;
- 极少数框架(如某些 React SPA)可能动态重写 body 子节点,建议将钥匙挂载到 document.documentElement(即 <html>)以获得更高稳定性;
- 生产环境建议增加 @run-at document-idle 或 @run-at document-start 元数据,确保尽早注入。
此方案兼顾简洁性与鲁棒性,是前端轻量级防误触场景的理想实践。

















