outside-click-js
Version:
79 lines (70 loc) • 2.21 kB
JavaScript
// 判断是否有windows对象(浏览器环境)
const HAS_WINDOWS = typeof window !== 'undefined'
// 判断是否有navigator
const HAS_NAVIGATOR = typeof navigator !== 'undefined'
// 是否支持touch
const IS_TOUCH =
HAS_WINDOWS && ('ontouchstart' in window || (HAS_NAVIGATOR && navigator.msMaxTouchPoints > 0))
// 默认的触发事件
const EVENTS = IS_TOUCH ? ['touchstart', 'click'] : ['click']
// 默认的拦截器
const MIDDLEWARE = e => e
// 缓存事件
const cacheListener = []
// 对外暴露函数
function clickOutside({ el, events, handler, middleware }) {
const params = processParams({ el, events, handler, middleware })
params.events.forEach(event => {
const instance = { event, el, handler: (e) => clickOutsideHandle(params.el, e, params.handler, params.middleware) }
cacheListener.push(instance)
onEvent(instance)
})
}
// 处理参数
function processParams({ el, events, handler, middleware }) {
return {
el,
handler,
events: events ? events : EVENTS,
middleware: middleware ? middleware : MIDDLEWARE
}
}
// 注册事件
function onEvent({ event, handler }) {
return setTimeout(() => document.addEventListener(event, handler), 0)
}
// 注销事件
function offEvent(event, handler) {
return document.removeEventListener(event, handler)
}
// 核心处理函数
function clickOutsideHandle(el, e, handler, middleware) {
// contains 传入的节点是否有调用节点的子节点
// 是否是当前节点的外部节点
const isClickOutside = e.target !== el && !el.contains(e.target)
// 内部节点就不做处理
if (!isClickOutside) return
// 中间拦截器,同时会执行
if (middleware(e, el)) {
handler(e, el)
}
}
// 对外注销函数
function removeClickOutside(targetElement) {
// 遍历已绑定的
cacheListener.forEach(({event, el, handler}) => {
// 如果没有指定元素,则全部事件注销
if (!targetElement) {
return offEvent(event, handler)
}
// 指定元素,则针对指定去注销
if (targetElement === el) {
return offEvent(event, handler)
}
})
}
const $outsideClick = {
clickOutside,
removeClickOutside
}
export default $outsideClick