clickoutside的几种实现

有关relatedTarget的二三事

spark1e ·

实现

一段最简单的clickoutside方案。其中contains兼容到IE9

function clickOutsideListener(targetDom, clickOutsideFn) {
        this.handler = function(e){   
            if (!targetDom.contains(e.target)){
              // Clicked outside the box
              clickOutsideFn && clickOutsideFn()
            }
        }
    }
    clickOutsideListener.prototype.add = function() {
        document.addEventListener('click', this.handler)
    };
    clickOutsideListener.prototype.remove = function (){
        document.removeEventListener('click', this.handler)
    }

那么我们在绑定的时候,就会通过类似的代码:

var $ = document.querySelector;
if ($('#menu')) $('#menu').addEventListener('mouseleave', mouseLeaveHandler);

这里要注意,如果你的鼠标移开事件元素内部有如select这一类的元素那可能在部分浏览器(如火狐)会有兼容性问题,解决方案:

function mouseLeaveHandler(e) {
	if (e.relatedTarget === null) return;
	// handler logic..
}

原因

relatedTarget是鼠标次要事件,因此和鼠标的一些行为(mouse*)密切相关,MDN给出的对应关系请见:

MDN - relatedTarget

在本次排查中,正是select点开菜单后,在Firefox下,移动到菜单内是一个丢失了relatedTarget的mouse事件,因此可以直接return掉来避免后续执行。

除此以外,mouseover和mouseout著名的bug,即进入mouseover元素的节点,会触发一次mouseout事件和一次mouseover事件,这时就会出现mouseout到里层节点(e.relatedTarget = 里层节点),紧接着mouseover到当前节点(e.relatedTarget = 当前节点),即出现了从自己mouseover到自己的bug。

其他实现

VueUse库

使用的composedPath,兼容到IE11,如果需要更好的兼容性请见:https://gist.github.com/sibbng/13e83b1dd1b733317ce0130ef07d4efd

clickoutside的几种实现
本文作者
spark1e
发布于
2024-08-13
许可协议
转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!
评论区 - Powered by Giscus