v-click-outside
介紹
使用在Vue
中的插件盒齿,一個(gè)附加的指令插件,作用是:
- 點(diǎn)擊指定元素外的區(qū)域可以觸發(fā)事件
- 可以綁定多個(gè)元素,一個(gè)元素上可以綁定多個(gè)事件
- 有中間件的概念,其實(shí)也就是個(gè)攔截器
核心實(shí)現(xiàn)代碼
function onEvent({ el, event, handler, middleware }) {
// 判斷點(diǎn)擊事件觸發(fā)的元素(event.target)是否是綁定元素(el)的子元素
const isClickOutside = event.target !== el && !el.contains(event.target)
// 如果不是外部則不觸發(fā)
if (!isClickOutside) {
return
}
// middleware是中間件(攔截器),根據(jù)返回值可以決定是否攔截肖粮,同時(shí)還會(huì)執(zhí)行middleware內(nèi)部的代碼
if (middleware(event, el)) {
// 綁定的點(diǎn)擊元素外區(qū)域的觸發(fā)函數(shù)
handler(event, el)
}
}
創(chuàng)建觸發(fā)事件的實(shí)例
對(duì)其中的參數(shù)和格式做處理,為簡化后續(xù)創(chuàng)建尔苦,處理后是一個(gè)對(duì)象涩馆,里面包含el
當(dāng)前綁定元素和eventHandlers
綁定事件的數(shù)組列表
而eventHandlers
中每個(gè)對(duì)象又包含event
事件名和handler
事件處理函數(shù)
-
el
:綁定事件的元素 -
events
:數(shù)組,可以綁定多個(gè)事件允坚,比如:blur,foucs等魂那,默認(rèn)是click(PC),移動(dòng)端是click和tap -
handler
:綁定的事件觸發(fā)的函數(shù) -
middleware
:中間件
function createInstance({ el, events, handler, middleware }) {
return {
el,
eventHandlers: events.map((eventName) => ({
event: eventName,
handler: (event) => onEvent({ event, el, handler, middleware }),
})),
}
}
初始化實(shí)例并綁定
這里面有2個(gè)小細(xì)節(jié):
- 采用定時(shí)器注冊(cè)事件稠项,實(shí)現(xiàn)類似異步注冊(cè)涯雅,提高效率
- 將創(chuàng)建好的實(shí)例存放在數(shù)組緩存為后續(xù)注銷和修改使用
function bind(el, { value }) {
// 處理參數(shù)和初始值問題
const { events, handler, middleware, isActive } = processDirectiveArguments(value)
// 是否可用的開關(guān)
if (!isActive) {
return
}
// 初始化實(shí)例
const instance = createInstance({ el, events, handler, middleware })
// 根據(jù)初始化后的實(shí)例,去循環(huán)綁定指定的事件和事件處理函數(shù)
instance.eventHandlers.forEach(({ event, handler }) =>
setTimeout(() => document.addEventListener(event, handler), 0),
)
// 存放在數(shù)組緩存中
directive.instances.push(instance)
}
注銷已初始化的實(shí)例
有兩點(diǎn)注意:
- 注銷事件時(shí)展运,不能用
setTimeout
活逆,因?yàn)樾枰饺孔N完后做清除緩存實(shí)例的動(dòng)作 -
removeEventListener
中的處理函數(shù)必須用addEventListener
中的處理函數(shù),所以這里緩存就起到作用了拗胜,如果只是單純的函數(shù)一樣是沒辦法注銷的
function removeInstance(el) {
// 讀取緩存中el的實(shí)例
const instanceIndex = directive.instances.findIndex((instance) => instance.el === el)
// 如果不存在實(shí)例則停止注銷
if (instanceIndex === -1) {
// Note: This can happen when active status changes from false to false
return
}
// 緩存中el的實(shí)例
const instance = directive.instances[instanceIndex]
// 循環(huán)注銷
instance.eventHandlers.forEach(({ event, handler }) =>
document.removeEventListener(event, handler),
)
// 清除緩存中的注銷實(shí)例
directive.instances.splice(instanceIndex, 1)
}
解讀源碼的原因
由于v-click-outside
只能在Vue
中以指令的蔗候,但是有時(shí)候可能用不了指令,比如動(dòng)態(tài)生成的元素埂软,也有可能不是在Vue
中想要用到锈遥,所以通過讀源碼開發(fā)一個(gè)原生js庫
最后發(fā)布了: