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