項(xiàng)目中element-ui popover外部點(diǎn)擊隱藏失效

問題

在項(xiàng)目頁面中使用 popover,設(shè)置trigger='click'時(shí)點(diǎn)擊外部不會(huì)觸發(fā)自動(dòng)隱藏,但在 element 官網(wǎng)中是可以正常觸發(fā)的(官方示例),項(xiàng)目中的菜單是自定義寫的,所以懷疑是有黑魔法。

查找原因

  1. 將 popover 寫在app.vue根組件內(nèi),發(fā)現(xiàn)可以正常觸發(fā)自動(dòng)隱藏。
  2. app.vue的 mounted 鉤子中加入window.addEventListener('click', () => console.log('window click===>>>>')),發(fā)現(xiàn)只有菜單欄外層能夠觸發(fā)。
  3. 檢查菜單欄組件,發(fā)現(xiàn)代碼中<div class="main" @click.stop="isShowWhole = false">,這里的 click 事件使用了 stop 修飾符(阻止冒泡),可能阻止了 popover 外部點(diǎn)擊的事件判斷,嘗試將 stop 修飾符去掉,發(fā)現(xiàn)外部點(diǎn)擊事件正常觸發(fā)。

確認(rèn)代碼修改沒有副作用

在修復(fù) bug 時(shí),需要注意不會(huì)產(chǎn)生額外的 bug,那就需要了解修改的這段代碼的含義

@click.stop="isShowWhole = false"

從代碼上看,點(diǎn)擊 class 為 main 的 div 將會(huì)觸發(fā)左邊側(cè)邊欄縮略顯示,加上 stop 修飾符是為了防止事件冒泡,所以能否去掉 stop 需要確認(rèn)是否有這個(gè)必要。

// router.js
let routes = [
    {
      path: '/',
      alias: '/admin',
      component: Menu,
      children: [...Pages],
    },
    {
      path: '*',
      name: '404',
      component: NotFound,
    },
  ];

在路由中可以看到,Menu 是作為根路由進(jìn)行渲染,除了 404 頁面都是它的子路由,所以 stop 修飾符是沒有必要加上的,去除后經(jīng)過測試沒有其他影響。

深入 element popover 源碼分析原因

對 element 組件進(jìn)行 debug 時(shí),可以直接引入相關(guān)組件的源碼

import ElPopover from 'element-ui/packages/popover';
export default {
    components: {
        CheckboxFilter,
        ElPopover
    },
    ...
}

然后我們就可以在node_modules的 element 源碼進(jìn)行 debug 操作(危險(xiǎn)步驟,debug 后需要復(fù)原)。

// node_modules/element-ui/packages/popover/src/main.vue
mounted() {
    ...
    if (this.trigger === 'click') {
      on(reference, 'click', this.doToggle);
      on(document, 'click', this.handleDocumentClick);
    } else if (this.trigger === 'hover') {
      ...
    } else if (this.trigger === 'focus') {
      ...
    }
}

popover 在 mounted 鉤子內(nèi)初始化了trigger='click'的事件綁定,on(document, 'click', this.handleDocumentClick)這里綁定了 document 很可能就是阻止事件冒泡后不能觸發(fā)外部點(diǎn)擊隱藏的判斷邏輯。

// node_modules/element-ui/packages/popover/src/main.vue
handleDocumentClick(e) {
      let reference = this.reference || this.$refs.reference;
      const popper = this.popper || this.$refs.popper;

      if (!reference && this.$slots.reference && this.$slots.reference[0]) {
        reference = this.referenceElm = this.$slots.reference[0].elm;
      }
      if (!this.$el ||
        !reference ||
        this.$el.contains(e.target) ||
        reference.contains(e.target) ||
        !popper ||
        popper.contains(e.target)) return;
      this.showPopper = false;
    },

這里判斷this.$el是否包含 click 的 target,從而是否觸發(fā)this.showPopper = false,當(dāng)菜單欄阻止事件冒泡后 document 不能監(jiān)聽到 click 事件,才會(huì)無法進(jìn)行外部點(diǎn)擊隱藏的判斷邏輯。

延伸v-clickoutside

element 的 select 組件中用到了 v-clickoutside 自定義指令,作用和 popover 的 handleDocumentClick 差不多(倒不如說 handleDocumentClick 是特殊的 clickoutside)

在上面的問題中,我們單獨(dú)把 v-clickoutside 抽出來使用確實(shí)可以的,這是為什么呢?

// node_modules/element-ui/packages/popover/src/utils/clickoutside.js
!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e));

!Vue.prototype.$isServer && on(document, 'mouseup', e => {
  nodeList.forEach(node => node[ctx].documentHandler(e, startClick));
});

答案是 v-clickoutside 使用鼠標(biāo)事件判斷的,所以 click 的 阻止冒泡不會(huì)讓 clickoutside 無效。

總結(jié)

解決 bug 的過程中需要做到不產(chǎn)生額外的 bug,并且深入分析問題的原因有助于能力的提高。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實(shí)現(xiàn)的。 ??事件,就是文檔或?yàn)g覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,684評論 1 11
  • VUE Vue :數(shù)據(jù)驅(qū)動(dòng)的M V Vm框架 m :model(后臺(tái)提供數(shù)據(jù)),v :view(頁面),vM(模板...
    wudongyu閱讀 5,524評論 0 11
  • vue概述 在官方文檔中,有一句話對Vue的定位說的很明確:Vue.js 的核心是一個(gè)允許采用簡潔的模板語法來聲明...
    li4065閱讀 7,598評論 0 25
  • Scatterplots ggplot Syntax Overplotting 對于散點(diǎn)圖出現(xiàn)重疊,我們可以用al...
    esskeetit閱讀 687評論 0 0
  • 1 簡凡的男友很丑。 大一剛?cè)胄r(shí),同在N大的姐姐簡萍和學(xué)院的新生聚餐,把簡凡也叫上了。簡凡并不想去,但姐姐勸告她...
    丁念閱讀 667評論 6 2

友情鏈接更多精彩內(nèi)容