Element-ui中元素滾動(dòng)時(shí)el-option超出元素區(qū)域的問(wèn)題
復(fù)現(xiàn)場(chǎng)景, 看圖
分析原因
為簡(jiǎn)單起見(jiàn), 把選項(xiàng)區(qū)域描述為popperEl
- popperEl的z-index 比較大, 會(huì)覆蓋在其他元素上面
- popperEl默認(rèn)是插入body元素的(可以將popper-append-to-body設(shè)為false后不插入到body)
- popperEl是在mouseup事件里去做隱藏邏輯的, 而按下鼠標(biāo), 移動(dòng)滾動(dòng)條的時(shí)候, 并沒(méi)有觸發(fā)mouseup事件.
- popperEl并沒(méi)有監(jiān)聽(tīng)滾動(dòng)事件(沒(méi)法監(jiān)聽(tīng), 也沒(méi)必要監(jiān)聽(tīng))
解決方案
方案一
我最初想到的解決方案是通過(guò)css解決,通過(guò)popper-class屬性給Select下拉框添加類名,然后用css來(lái)做, 試了一下這個(gè)方案并不可行(只能在某些特定的場(chǎng)景下起作用),遂放棄,可能最優(yōu)雅最高性能的方法就是用css來(lái)搞定, 有踩過(guò)這個(gè)坑的朋友請(qǐng)指點(diǎn)一下
方案二
通過(guò)監(jiān)聽(tīng)$root的scroll事件,利用事件冒泡,只需要在根元素上添加scroll事件的監(jiān)聽(tīng)就可以了, 測(cè)試一番之后, 發(fā)現(xiàn)scroll事件根本不支持冒泡, event.bubbles為false)。
方案三
通過(guò)查看element-ui 的select.vue, 發(fā)現(xiàn)控制popperEl顯隱的是visible 和 emptyText這兩個(gè)實(shí)例屬性, 很明顯, emptytext是不能動(dòng)的, 只能在visible上動(dòng)手腳了. 這里放一小段源碼
<transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="doDestroy"> <el-select-menu ref="popper" :append-to-body="popperAppendToBody" v-show="visible && emptyText !== false"> <el-scrollbar tag="ul" wrap-class="el-select-dropdown__wrap" view-class="el-select-dropdown__list" ref="scrollbar" :class="{ 'is-empty': !allowCreate && query && filteredOptionsCount === 0 }" v-show="options.length > 0 && !loading"> <el-option :value="query" created v-if="showNewOption"> </el-option> <slot></slot> </el-scrollbar> <p class="el-select-dropdown__empty" v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0 ))"> {{ emptyText }} </p> </el-select-menu> </transition>
全局搜索this.visible, 發(fā)現(xiàn)了這個(gè)方法
handleClose() { this.visible = false; },
這下好辦了, 按圖索驥, 順藤摸瓜, 找到這個(gè)
<template> <div class="el-select" :class="[selectSize ? 'el-select--' + selectSize : '']" @click.stop="toggleMenu" v-clickoutside="handleClose"> 后面的省略...
找到v-clickoutside指令之后, 豁然開(kāi)朗 原來(lái)點(diǎn)擊其他區(qū)域的時(shí)候, popperEl會(huì)自動(dòng)關(guān)閉的奧秘在這里, 結(jié)合方案二的靈感, 現(xiàn)給出如下代碼.
// src/mixins/fackClickOutSide.js let lock = true; let el = null; const MousedownEvent = new Event('mousedown', {bubbles:true}); const MouseupEvent = new Event('mouseup', {bubbles:true}); const fakeClickOutSide = () => { document.dispatchEvent(MousedownEvent); document.dispatchEvent(MouseupEvent); lock = true; // console.log('dispatchEvent'); }; const mousedownHandle = e => { let classList = e.target.classList; if(classList.contains('el-select__caret') || classList.contains('el-input__inner')) { lock = false; return; } if(lock) return; fakeClickOutSide(); }; const mousewheelHandle = e => { if(lock || e.target.classList.contains('el-select-dropdown__item') || e.target.parentNode.classList.contains('el-select-dropdown__item')) return; fakeClickOutSide(); }; const eventListener = (type) => { el[type + 'EventListener']('mousedown', mousedownHandle); window[type + 'EventListener']('mousewheel', mousewheelHandle); window[type + 'EventListener']('DOMMouseScroll', mousewheelHandle); // fireFox 3.5+ } export default { mounted() { el = this.$root.$el; el.addFakeClickOutSideEventCount = el.addFakeClickOutSideEventCount || 0; (! el.addFakeClickOutSideEventCount) && this.$nextTick(() => { eventListener('add'); }); el.addFakeClickOutSideEventCount += 1; }, destroyed() { eventListener('remove'); el.addFakeClickOutSideEventCount -= 1; }, }
使用姿勢(shì)
建議在根組件上混合進(jìn)去, 當(dāng)然,你也可以在需要的組件上去混合(不太建議, 這點(diǎn)代碼性能損耗應(yīng)該不大吧, 哈哈哈)
// src/App.vue import fakeClickOutSide from '@/mixins/fakeClickOutSide.js' export default { name: 'App', mixins: [fakeClickOutSide], }
測(cè)試
常規(guī)基礎(chǔ)用法 和 自定義模板用法(模板內(nèi)沒(méi)有嵌套的標(biāo)簽) 均完美通過(guò).
自定義模板內(nèi)如果嵌套多級(jí)標(biāo)簽, 需要在標(biāo)簽上添加標(biāo)記,然后在mousewheel事件回調(diào)里判斷是否有這個(gè)標(biāo)記.
總結(jié)
依然存在的問(wèn)題(隱患):
- 在mousewheel事件回調(diào)沒(méi)有做節(jié)流, 考慮到有鎖, 且滾輪事件觸發(fā)的頻率也不是很高(相對(duì)于mousemove事件來(lái)講), 性能消耗并不大, 遂不做節(jié)流(主要是懶).
- 在mousewheel事件回調(diào)里,判斷event.target 是否是在popperEl元素內(nèi)部的方法感覺(jué)不是很靠譜, 且效率不高, 在mousedown 事件里判斷是不是el-select元素的方法也存在同樣的隱患, 后期再想辦法修改(修改是不可能修改的, 又不是不能用).
- 在自定義模板用法里, 如果有嵌套的標(biāo)簽, 那么在mousewheel事件回調(diào)里判斷event.target 是不是在popperEl元素內(nèi)部的方法就崩潰了(這是個(gè)雷), 目前的解決辦法是手動(dòng)在嵌套的標(biāo)簽上都加上一個(gè)標(biāo)記, 在事件里,添加這個(gè)標(biāo)記的判斷, 但是這種做法對(duì)于已經(jīng)編寫(xiě)完成的模板無(wú)效, 只能再次修改, 考慮過(guò)使用遞歸向上查找, 但是效率不高, 性能消耗太大, 且自定義el-option模板這種情況在我們現(xiàn)階段的業(yè)務(wù)中幾乎不存在, 所以就沒(méi)有考慮這個(gè)bug.
感謝一位大佬長(zhǎng)期以來(lái)給予的幫助.
相關(guān)文章
vue 子組件watch監(jiān)聽(tīng)不到prop的解決
這篇文章主要介紹了vue 子組件watch監(jiān)聽(tīng)不到prop的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-08-08Vue3實(shí)現(xiàn)Message消息組件示例
在大多數(shù) web 產(chǎn)品中,全局的 Message 組件占有較大的使用場(chǎng)景,本文主要介紹了Vue3實(shí)現(xiàn)Message消息組件示例,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06vue將毫秒數(shù)轉(zhuǎn)化為正常日期格式的實(shí)例
今天小編就為大家分享一篇vue將毫秒數(shù)轉(zhuǎn)化為正常日期格式的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-09-09vue quill editor 使用富文本添加上傳音頻功能
vue-quill-editor 是vue項(xiàng)目中常用的富文本插件,其功能能滿足大部分的項(xiàng)目需求。這篇文章主要介紹了vue-quill-editor 富文本添加上傳音頻功能,需要的朋友可以參考下2020-01-01關(guān)于Vue新搭檔TypeScript快速入門(mén)實(shí)踐
這篇文章主要介紹了關(guān)于Vue新搭檔TypeScript快速入門(mén)實(shí)踐,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09Vue.js中的extend綁定節(jié)點(diǎn)并顯示的方法
在本篇內(nèi)容里小編給大家整理了關(guān)于Vue.js中的extend綁定節(jié)點(diǎn)并顯示的方法以及相關(guān)知識(shí)點(diǎn),需要的朋友們學(xué)習(xí)下。2019-06-06