相邻兄弟层元素如何实现点击穿透

677 阅读1分钟

由于最近公司项目里的排位赛ui,会存在多个层级间的覆盖。一个最典型案列就是,在弹出排位赛的排名界面ui,同时会在页面上弹出弹幕,有些用户的操作点是在弹幕下,需要穿透弹幕实现点击操作,本文就这个问题列举些部分解决方法。

1、使用css3新属性:pointer-events

  • pointer-events:none: 元素永远不会成为鼠标事件的target (en-US)。但是,当其后代元素的pointer-events属性指定其他值时,鼠标事件可以指向后代元素,在这种情况下,鼠标事件将在捕获或冒泡阶段触发父元素的事件侦听器。
  • 通过使用none定义,快速使用在两个兄弟级别的ui层。通过把上一层设置pointer-events设置位none,就可以实现第一层级没有点击效果,第二层可以触发点击效果
  • 兼容性

image.png

  • 简易案例如下:
        <style>
            .level1 {
                position: absolute;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: yellow;
            }
            .level2 {
                position: absolute;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: rgb(0, 0, 0, 0.7);
                /*pointer-events: none;*//*取消注释就可以使用*/
            }
            button {
                position: absolute;
                width: 200px;
                height: 200px;
            }
            .level1>button {
                right: 50%;
            }
            .level2>button {
                right: 0;
            }
        </style>
    <div class="level1">
        <button onclick="alert('level1-btn')">level1-btn</button>
    </div>
    <div class="level2">
        <button onclick="alert('level2-btn')">level2-btn</button>
    </div>

8nusa-2h7zg (1).gif

2、js实现

  • 之前css3还没有这个属性的时候,就只能用js去实现,主要原理通过记录点击的坐标,然后与其他层点击元素的坐标比较,如果当前点击的坐标属于该元素的坐标区间,就通过js去触发该元素的点击事件
  • 简易代码如下:
<body>
        <style>
            .level1 {
                position: absolute;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: yellow;
            }
        
            .level2 {
                position: absolute;
                top: 0;
                width: 100%;
                height: 100%;
                background-color: rgb(0, 0, 0, 0.7);
            }
        
            button {
                position: absolute;
                width: 200px;
                height: 200px;
            }
        
            .level1>button {
                right: 50%;
            }
        
            .level2>button {
                right: 0;
            }
        </style>
    <div class="level1">
        <button>level1-btn</button>
    </div>
    <div class="level2">
        <button>level2-btn</button>
    </div>
</body>
<script>
    const isClickOnElement = (element, x, y) => {
        var rect = element.getBoundingClientRect(); //获取元素的坐标轴范围
        if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) {
            return true;
        } else {
            return false;
        }
    }
    let clickEvent = () => { };
    let touchEvent = () => { };
    const addPenetrateListener = (element, func) => {
        clickEvent = (event) => {
            var x = event.pageX;
            var y = event.pageY;
            console.log('X坐标:' + x + ', Y坐标:' + y, isClickOnElement(element, x, y));
            isClickOnElement(element, x, y) && (func.call(element))
        }
        document.addEventListener('click', clickEvent);
        touchEvent = (event) => {
            var touch = event.touches[0];
            var x = touch.pageX;
            var y = touch.pageY;
            console.log('X坐标:' + x + ', Y坐标:' + y, isClickOnElement(element, x, y));
            isClickOnElement(element, x, y) && (func.call(element))
        }
        document.addEventListener('touchstart', touchEvent);
    }
    addPenetrateListener(document.querySelector(".level1 button"), () => { alert('level1') });
    window.onunload = () => {
        document.removeEventListener('click', clickEvent);
        document.removeEventListener('touchstart', clickEvent);
    }
</script>

  • 实现效果如上视频类似

3、参考:

pointer-events原理

pointer-events其他案列