Element-ui中元素滚动时el-option超出元素区域的问题


Posted in Javascript onMay 30, 2019

复现场景, 看图

Element-ui中元素滚动时el-option超出元素区域的问题

分析原因

为简单起见, 把选项区域描述为popperEl

  • popperEl的z-index 比较大, 会覆盖在其他元素上面
  • popperEl默认是插入body元素的(可以将popper-append-to-body设为false后不插入到body)
  • popperEl是在mouseup事件里去做隐藏逻辑的, 而按下鼠标, 移动滚动条的时候, 并没有触发mouseup事件.
  • popperEl并没有监听滚动事件(没法监听, 也没必要监听)

解决方案

 方案一

我最初想到的解决方案是通过css解决,通过popper-class属性给Select下拉框添加类名,然后用css来做, 试了一下这个方案并不可行(只能在某些特定的场景下起作用),遂放弃,可能最优雅最高性能的方法就是用css来搞定, 有踩过这个坑的朋友请指点一下

方案二

通过监听$root的scroll事件,利用事件冒泡,只需要在根元素上添加scroll事件的监听就可以了, 测试一番之后, 发现scroll事件根本不支持冒泡, event.bubbles为false)。

方案三

通过查看element-ui 的select.vue, 发现控制popperEl显隐的是visible 和 emptyText这两个实例属性, 很明显, emptytext是不能动的, 只能在visible上动手脚了. 这里放一小段源码

<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, 发现了这个方法

handleClose() {
  this.visible = false;
},

这下好办了, 按图索骥, 顺藤摸瓜, 找到这个

<template>
 <div
  class="el-select"
  :class="[selectSize ? 'el-select--' + selectSize : '']"
  @click.stop="toggleMenu"
  v-clickoutside="handleClose">
  后面的省略...

找到v-clickoutside指令之后, 豁然开朗 原来点击其他区域的时候, popperEl会自动关闭的奥秘在这里, 结合方案二的灵感, 现给出如下代码.

// 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;
 },
}

使用姿势

建议在根组件上混合进去, 当然,你也可以在需要的组件上去混合(不太建议, 这点代码性能损耗应该不大吧, 哈哈哈)

// src/App.vue
import fakeClickOutSide from '@/mixins/fakeClickOutSide.js'
export default {
  name: 'App',
  mixins: [fakeClickOutSide],
}

测试

常规基础用法 和 自定义模板用法(模板内没有嵌套的标签) 均完美通过.

自定义模板内如果嵌套多级标签, 需要在标签上添加标记,然后在mousewheel事件回调里判断是否有这个标记.

总结

依然存在的问题(隐患):

  • 在mousewheel事件回调没有做节流, 考虑到有锁, 且滚轮事件触发的频率也不是很高(相对于mousemove事件来讲), 性能消耗并不大, 遂不做节流(主要是懒).
  • 在mousewheel事件回调里,判断event.target 是否是在popperEl元素内部的方法感觉不是很靠谱, 且效率不高, 在mousedown 事件里判断是不是el-select元素的方法也存在同样的隐患, 后期再想办法修改(修改是不可能修改的, 又不是不能用).
  • 在自定义模板用法里, 如果有嵌套的标签, 那么在mousewheel事件回调里判断event.target 是不是在popperEl元素内部的方法就崩溃了(这是个雷), 目前的解决办法是手动在嵌套的标签上都加上一个标记, 在事件里,添加这个标记的判断, 但是这种做法对于已经编写完成的模板无效, 只能再次修改, 考虑过使用递归向上查找, 但是效率不高, 性能消耗太大, 且自定义el-option模板这种情况在我们现阶段的业务中几乎不存在, 所以就没有考虑这个bug.

感谢一位大佬长期以来给予的帮助.

Javascript 相关文章推荐
tangram框架响应式加载图片方法
Nov 21 Javascript
js 动态加载事件的几种方法总结
Dec 25 Javascript
基于BootStrap Metronic开发框架经验小结【一】框架总览及菜单模块的处理
May 12 Javascript
让编辑器支持word复制黏贴、截屏的js代码
Oct 17 Javascript
jQuery实现的简单排序功能示例【冒泡排序】
Jan 13 Javascript
React学习之事件绑定的几种方法对比
Sep 24 Javascript
微信小程序列表渲染功能之列表下拉刷新及上拉加载的实现方法分析
Nov 27 Javascript
vue addRoutes实现动态权限路由菜单的示例
May 15 Javascript
JavaScript中的事件与异常捕获详析
Feb 24 Javascript
微信小程序实现多行文字超出部分省略号显示功能
Oct 23 Javascript
解决vscode进行vue格式化,会自动补分号和双引号的问题
Oct 26 Javascript
vue修饰符.capture和.self的区别
Apr 22 Vue.js
轻松学习JavaScript函数中的 Rest 参数
May 30 #Javascript
细说Vue组件的服务器端渲染的过程
May 30 #Javascript
了解JavaScript中let语句
May 30 #Javascript
koa+jwt实现token验证与刷新功能
May 30 #Javascript
深入理解JavaScript 箭头函数
May 30 #Javascript
socket在egg中的使用实例代码详解
May 30 #Javascript
深入了解JavaScript 私有化
May 30 #Javascript
You might like
php下连接ftp实现文件的上传、下载、删除文件实例代码
2010/06/03 PHP
php中filter_input函数用法分析
2014/11/15 PHP
PHP使用preg_split()分割特殊字符(元字符等)的方法分析
2017/02/04 PHP
PHP实现的字符串匹配算法示例【sunday算法】
2017/12/19 PHP
jQuery点击弹出下拉菜单的小例子
2013/08/01 Javascript
js的参数有长度限制吗?发现不能超过2083个字符
2014/04/20 Javascript
Bootstrap表格和栅格分页实例详解
2016/05/20 Javascript
JavaScript类型系统之布尔Boolean类型详解
2016/06/26 Javascript
jquery 属性选择器(匹配具有指定属性的元素)
2016/09/06 Javascript
微信小程序  生命周期详解
2016/10/27 Javascript
vue组件 $children,$refs,$parent的使用详解
2017/07/31 Javascript
Vue-Router模式和钩子的用法
2018/02/28 Javascript
Node.js 利用cheerio制作简单的网页爬虫示例
2018/03/01 Javascript
Webpack 之 babel-loader文件预处理器详解
2018/03/23 Javascript
微信小程序发送短信验证码完整实例
2019/01/07 Javascript
JQuery判断radio单选框是否选中并获取值的方法
2019/01/17 jQuery
JS如何生成动态列表
2020/09/22 Javascript
JS实现页面侧边栏效果探究
2021/01/08 Javascript
Python中几个比较常见的名词解释
2015/07/04 Python
Python中join函数简单代码示例
2018/01/09 Python
详解Numpy数组转置的三种方法T、transpose、swapaxes
2019/05/27 Python
利用python在大量数据文件下删除某一行的例子
2019/08/21 Python
python-序列解包(对可迭代元素的快速取值方法)
2019/08/24 Python
DVF官方网站:美国时装界尊尚品牌
2017/08/29 全球购物
KENZO官网:高田贤三在法国创立的品牌
2019/05/16 全球购物
焊接专业毕业生求职信
2013/10/01 职场文书
中国好声音广告词
2014/03/18 职场文书
担保书怎么写
2014/04/01 职场文书
宪法宣传周工作方案
2014/05/26 职场文书
镇班子对照检查材料思想汇报
2014/09/24 职场文书
小学生安全教育广播稿
2014/10/20 职场文书
新员工试用期自我评价
2015/03/10 职场文书
2015年城管个人工作总结
2015/05/15 职场文书
初级职称评定工作总结
2015/08/13 职场文书
女性励志书籍推荐
2019/08/19 职场文书
导游词之南京中山陵
2019/11/27 职场文书