无法用 ::selection 配合兄弟选择器实现选中时其它部分变暗,因 ::selection 仅作用于选中文本本身;需监听 selectionchange 事件,结合 window.getSelection() 判断选区并动态添加遮罩层。

用 ::selection 配合兄弟选择器无法实现“选中时其它部分变暗”
浏览器原生的 ::selection 伪元素只能作用于被选中的文本本身,不能反向影响页面其他元素。所以直接写 ::selection { background: #000; } 只会让选中文字变黑,其余内容完全不受控。
真正可行的路径是:监听 selectionchange 事件,手动给非选中区域加遮罩或降透明度。
监听 selectionchange 并判断选区范围
这个事件在用户释放鼠标/结束键盘选中时触发,但要注意它不带参数,得主动调用 window.getSelection() 获取当前选区。
-
window.getSelection()返回Selection对象,若为空(.rangeCount === 0),说明没选中任何内容 - 用
getRangeAt(0)拿到首个选区范围,再用range.commonAncestorContainer判断选中是否落在目标文本容器内 - 别直接用
document.onselectstart—— 它只在选中开始前触发,拿不到最终选区,且会被preventDefault干扰
给非选中区域加半透明遮罩的实操方式
最稳定的做法是:在 body 下插入一个全屏 div 遮罩层,仅当有有效选中时显示,并把选中文本所在的容器设为相对定位+高 z-index,让它浮在遮罩之上。
立即学习“前端免费学习笔记(深入)”;
示例关键逻辑:
document.addEventListener('selectionchange', () => {
const sel = window.getSelection();
const overlay = document.getElementById('selection-overlay');
const targetEl = document.querySelector('.target-text');
if (sel.rangeCount > 0 && sel.containsNode(targetEl, true)) {
overlay.style.display = 'block';
targetEl.style.position = 'relative';
targetEl.style.zIndex = '10';
} else {
overlay.style.display = 'none';
targetEl.style.zIndex = '';
}
});
注意:sel.containsNode(targetEl, true) 的第二个参数必须为 true,否则子文本节点不被识别为“包含”。
CSS 遮罩层要避开滚动与层级陷阱
遮罩层如果用 position: fixed,页面滚动时会盖住选中文本;如果用 absolute,又受限于父容器定位上下文。稳妥方案是:
- 遮罩层设为
position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; - 给目标文本容器加
isolation: isolate,防止其被遮罩层的opacity或mix-blend-mode影响 - 避免对遮罩层用
opacity: 0.7—— 这会让内部所有子元素(包括浮上来的文本)也变暗;改用background: rgba(0,0,0,0.7)
复杂点在于:iframe、contenteditable 区域、Shadow DOM 内的文本,containsNode 可能返回 false,需要额外遍历 range 的 startContainer 和 endContainer 来判断归属。










