实现点击穿透父div触发子div点击事件且不影响父div的::before属性
问题
我开发了一款带有“手电筒”效果的游戏:蓝色半透明盒子(#puzzle3bg)通过手电筒区域显示内部移动的圆圈。现在需要保留手电筒效果的同时,让点击能穿透#puzzle3bg,触发下方圆圈元素的onclick事件。尝试使用pointer-events属性时,要么手电筒效果失效,要么子元素仍无法响应点击,求解决方法。
原代码
HTML
<div id="puzzle3"> <div id="puzzle3box"> <div class="p3correctCircle"></div> <div class="p3correctCircle"></div> <div class="p3correctCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> </div> <div id="puzzle3bg"></div> </div>
CSS
#puzzle3 { display: inline-block; text-align: center; } #puzzle3bg { display: block; width: 750px; height: 750px; --cursorX: 375px; --cursorY: 375px; } #puzzle3bg::before { content: ""; display: block; width: 750px; height: 750px; position: relative; background: radial-gradient( circle 100px at var(--cursorX) var(--cursorY), rgba(170,255,255,.3) 0%, rgba(170,255,255,.6) 80%, rgba(170,255,255,1) 100% ); z-index: 10; } .p3correctCircle { display: inline-block; position: absolute; background: #00FF00; border-radius: 50%; width: 50px; height: 50px; transition-duration: 600ms; z-index: 0; } .p3wrongCircle { display: inline-block; position: absolute; background: #FF0000; border-radius: 50%; width: 50px; height: 50px; transition-duration: 600ms; z-index: 0; }
JS
function updateCursor(e) { var x = e.offsetX var y = e.offsetY document.getElementById("puzzle3bg").style.setProperty('--cursorX', x + 'px') document.getElementById("puzzle3bg").style.setProperty('--cursorY', y + 'px') } document.getElementById("puzzle3bg").addEventListener('mousemove', updateCursor) document.getElementById("puzzle3bg").addEventListener('touchmove', updateCursor) // I took this from someone else's codepen so I'm not sure how to modify it
解决方案
核心问题是原代码中#puzzle3bg及其伪元素层级过高,且未正确设置点击穿透属性,导致遮挡了下方圆圈的点击事件。以下是具体修改方案:
修改后的代码
HTML(结构无需调整)
<div id="puzzle3"> <div id="puzzle3box"> <div class="p3correctCircle"></div> <div class="p3correctCircle"></div> <div class="p3correctCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> <div class="p3wrongCircle"></div> </div> <div id="puzzle3bg"></div> </div>
CSS
#puzzle3 { display: inline-block; text-align: center; position: relative; /* 设置为相对定位,作为内部绝对元素的容器 */ } #puzzle3bg { display: block; width: 750px; height: 750px; --cursorX: 375px; --cursorY: 375px; position: absolute; /* 绝对定位覆盖在圆圈容器上方 */ top: 0; left: 0; pointer-events: none; /* 允许点击穿透到下方元素 */ } #puzzle3bg::before { content: ""; display: block; width: 100%; /* 用百分比代替固定值,适配父容器 */ height: 100%; position: absolute; /* 修改为绝对定位,确保遮罩层正确覆盖 */ background: radial-gradient( circle 100px at var(--cursorX) var(--cursorY), rgba(170,255,255,.3) 0%, rgba(170,255,255,.6) 80%, rgba(170,255,255,1) 100% ); z-index: 10; } #puzzle3box { position: relative; /* 设置相对定位 */ z-index: 1; /* 确保圆圈容器在遮罩层下方 */ width: 750px; height: 750px; } .p3correctCircle { display: inline-block; position: absolute; background: #00FF00; border-radius: 50%; width: 50px; height: 50px; transition-duration: 600ms; z-index: 0; pointer-events: auto; /* 显式允许接收点击事件 */ } .p3wrongCircle { display: inline-block; position: absolute; background: #FF0000; border-radius: 50%; width: 50px; height: 50px; transition-duration: 600ms; z-index: 0; pointer-events: auto; /* 显式允许接收点击事件 */ }
JS
function updateCursor(e) { // 计算相对于#puzzle3的坐标,因为事件现在绑定在容器上 const containerRect = document.getElementById("puzzle3").getBoundingClientRect(); const x = e.clientX - containerRect.left; const y = e.clientY - containerRect.top; document.getElementById("puzzle3bg").style.setProperty('--cursorX', x + 'px') document.getElementById("puzzle3bg").style.setProperty('--cursorY', y + 'px') } // 将鼠标/触摸事件绑定到#puzzle3容器上(#puzzle3bg已设置pointer-events:none,无法触发事件) const puzzleContainer = document.getElementById("puzzle3"); puzzleContainer.addEventListener('mousemove', updateCursor) puzzleContainer.addEventListener('touchmove', updateCursor) // 给圆圈添加点击事件示例 document.querySelectorAll('.p3correctCircle, .p3wrongCircle').forEach(circle => { circle.addEventListener('click', function() { console.log('点击了', this.classList.contains('p3correctCircle') ? '正确圆圈' : '错误圆圈'); // 在这里添加你的点击逻辑 }); });
修改关键点说明
- 定位调整:给#puzzle3设置
position: relative,让内部绝对定位的元素以它为参考;#puzzle3bg设置position: absolute,覆盖在圆圈容器上方,实现遮罩效果。 - 点击穿透:给#puzzle3bg设置
pointer-events: none,让它不拦截任何点击、鼠标移动等事件,点击会直接穿透到下方的圆圈元素。 - 事件绑定转移:因为#puzzle3bg无法再触发鼠标移动事件,所以把事件绑定到#puzzle3容器上,重新计算鼠标坐标确保手电筒位置正确。
- 圆圈点击权限:给圆圈显式设置
pointer-events: auto,确保它们能正常接收点击事件。
内容的提问来源于stack exchange,提问作者JoshEco4
相关产品推荐
相关产品推荐

