ElementUI中的Cascader級聯選擇器或者TimePicker,DatePicker選擇器點擊後的浮層都是直接插入body標籤下的,elementUI內部會自動計算一個合適的點將浮層絕對定位上去。vue
可是最近公司在使用ElementUI框架的項目中,由於一個特殊需求不能讓這類組件的浮層脫離.vue的功能組件,因此浮層默認插入body下的方式確定是不知足需求的。jquery
ELementUI中的Select選擇器組件提供了一個屬性能夠控制下拉菜單是否插入到body中:
因此對於select只須要將該屬性設置爲false便可,但對於Cascader,TimePicker,DatePicker這些組件是沒有這個屬性的,對於這個問題我思考許久,耗費九成功力,想出瞭如下這種方式去模擬實現。app
範例:將Cascader組件浮層的父節點設置id爲wrap的節點(爲突出重點,範例中一些於此無關的代碼就省略了)框架
<div class="wrap" id="wrap"> <el-cascader id="broadcast_type" @visible-change="visibleChange" popper-class="floating-layer"//給浮層設置類名 :props="{ expandTrigger: 'hover' }" ......//省略 ></el-cascader> </div>
import $ from 'jquery' export default { data() { return { timer: null, flag: true,//鎖,控制不讓手動觸發浮層的時候再進入定時器 ......//省略 } }, methods: { visibleChange(e) { if (e&&this.flag) { this.flag = false this.timer = setInterval(() => { let floatingLayer = document.getElementsByClassName('floating-layer')[0]; if (floatingLayer&&floatingLayer.style.display==="") {//拿到浮層dom節點,可是不能是隱藏狀態 document.getElementById('wrap').appendChild(floatingLayer)//將浮層插入wrap節點下 $(document.getElementById('el_cascader')).trigger('click')//第一次收起浮層 $(document.getElementById('el_cascader')).trigger('click')//第二次打開浮層 clearInterval(this.timer) this.timer = null } },300) } else { this.flag = true clearInterval(this.timer) this.timer = null } }, } }
若是是TimePicker,DatePicker選擇器的話,由於沒有相似於Cascader組件的visible-change事件,可是能夠focus和blur事件去代替,不過思想都是相同的。
TimePicker,DatePicker選擇器在浮層父節點變化以後定位的數值彷佛是基於HTML的,因此在wrap下天然會有誤差,所以須要加一個改正數,或者直接將定位該爲fixed定位。Cascader組件彷佛沒有這個問題,也沒有去深究,下面是簡化代碼。dom
<template> <div class="wrap" id="wrap"> <el-form-item label="開始時間"> <el-col :span="11"> <el-date-picker id="broadcast_startdate" @focus="startDateFocus" @blur="startdateBlur" popper-class="broadcast-start-date-drop" type="date" placeholder="選擇日期" v-model="form.startDate" style="width: 100%;" ></el-date-picker> </el-col> <el-col class="line" :span="2">-</el-col> <el-col :span="11"> <el-time-picker id="broadcast_starttime" @focus="startTimeFocus" @blur="startTimeBlur" popper-class="broadcast-start-time-drop" placeholder="選擇時間" arrow-control v-model="form.startTime" style="width: 100%;" ></el-time-picker> </el-col> </el-form-item> </div> </template> <script> import $ from 'jquery' export default { data() { return { startDateTimer: null, startDateFlag: true, startTimeTimer: null, startTimeFlag: true, ......//省略 } }, methods: { startDateFocus() { if (this.startDateFlag) { this.startDateFlag = false this.startDateTimer = setInterval(() => { let startDateDrop = document.getElementsByClassName('broadcast-start-date-drop')[0]; if (startDateDrop && startDateDrop.style.display==="") { document.getElementById('wrap').appendChild(startDateDrop) $(document.getElementById('broadcast_startdate')).trigger('click') $(document.getElementById('broadcast_startdate')).trigger('click') startDateDrop.style.position = "fixed"//將定位改成fiexd定位 clearInterval(this.startDateTimer) this.startDateTimer = null } },100) } }, startdateBlur() { this.startDateFlag = true clearInterval(this.startDateTimer) this.startDateTimer = null }, startTimeFocus() { if (this.startTimeFlag) { this.startTimeFlag = false this.startTimeTimer = setInterval(() => { let startTimeDrop = document.getElementsByClassName('broadcast-start-time-drop')[0]; if (startTimeDrop && startTimeDrop.style.display==="") { document.getElementById('wrap').appendChild(startTimeDrop) $(document.getElementById('broadcast_starttime')).trigger('click') $(document.getElementById('broadcast_starttime')).trigger('click') startTimeDrop.style.position = "fixed" clearInterval(this.startTimeTimer) this.startTimeTimer = null } },100) } }, startTimeBlur() { this.startTimeFlag = true clearInterval(this.startTimeTimer) this.startTimeTimer = null }, } } </script>
當前項目我就用到這幾個組件,其它組件也不須要這樣特殊處理,不過我以爲這個思想是能夠行的通的,須要的小夥伴們能夠本身搞一下。上面提到的有的內部原理都是個人猜想沒有看源碼具體論證,反正這種方式解決了個人問題,也不知道還有沒有什麼好的方法,有想法的同窗們歡迎給我留言討論。this