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 相关文章推荐
Javascript select下拉框操作常用方法
Nov 09 Javascript
javascript下判断一个元素是否存在的代码
Mar 05 Javascript
js数值和和字符串进行转换时可以对不同进制进行操作
Mar 05 Javascript
jquery渐隐渐显的图片幻灯闪烁切换实现方法
Feb 26 Javascript
JQUERY的AJAX请求缓存里的数据问题处理
Feb 23 Javascript
Bootstrap进度条实现代码解析
Mar 07 Javascript
AngularJS使用ng-class动态增减class样式的方法示例
May 18 Javascript
Vue实现typeahead组件功能(非常靠谱)
Aug 26 Javascript
js实现单张图片平移切换效果
Oct 11 Javascript
详解Vue用cmd创建项目
Feb 12 Javascript
JavaScript中的惰性载入函数及优势
Feb 18 Javascript
JS实现多选框的操作
Jun 24 Javascript
轻松学习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
晶体管来复再生式二管收音机
2021/03/02 无线电
php动态添加url查询参数的方法
2015/04/14 PHP
PHP实现即时输出、实时输出内容方法
2015/05/27 PHP
PHP实现基于状态的责任链审批模式详解
2019/05/31 PHP
可拖动窗口,附带鼠标控制渐变透明,开启关闭功能
2006/06/26 Javascript
jQuery代码优化之基本事件
2011/11/01 Javascript
extjs ColumnChart设置不同的颜色实现代码
2013/05/17 Javascript
纯JS实现根据CSS的class选择DOM
2014/03/22 Javascript
JS函数重载的解决方案
2014/05/13 Javascript
了不起的node.js读书笔记之node的学习总结
2014/12/22 Javascript
jQuery简单几行代码实现tab切换
2015/03/10 Javascript
Chrome不支持showModalDialog模态对话框和无法返回returnValue问题的解决方法
2016/10/30 Javascript
JavaScript继承定义与用法实践分析
2018/05/28 Javascript
详解vue-cli3使用
2018/08/14 Javascript
微信小程序MUI导航栏透明渐变功能示例(通过改变opacity实现)
2019/01/24 Javascript
js中Function引用类型常见有用的方法和属性详解
2019/12/11 Javascript
微信小程序自定义菜单切换栏tabbar组件代码实例
2019/12/30 Javascript
JS异步宏队列微队列原理详解
2020/09/09 Javascript
jquery实现加载更多&quot;转圈圈&quot;效果(示例代码)
2020/11/09 jQuery
javascript实现电商放大镜效果
2020/11/23 Javascript
[55:39]DOTA2-DPC中国联赛 正赛 VG vs LBZS BO3 第二场 1月19日
2021/03/11 DOTA
举例讲解Python中的迭代器、生成器与列表解析用法
2016/03/20 Python
python list元素为tuple时的排序方法
2018/04/18 Python
pycharm打开命令行或Terminal的方法
2019/01/16 Python
使用Python向C语言的链接库传递数组、结构体、指针类型的数据
2019/01/29 Python
Python 离线工作环境搭建的方法步骤
2019/07/29 Python
Python+OpenCV 实现图片无损旋转90°且无黑边
2019/12/12 Python
Python Excel vlookup函数实现过程解析
2020/06/22 Python
美国气象仪器、花园装饰和墙壁艺术商店:Wind & Weather
2019/05/29 全球购物
荷兰在线啤酒店:Beerwulf
2019/08/26 全球购物
副厂长岗位职责
2014/02/02 职场文书
优秀工会工作者事迹材料
2014/06/02 职场文书
献爱心大型公益活动策划方案
2014/09/15 职场文书
小学语文课《掌声》教学反思
2016/03/03 职场文书
利用Python网络爬虫爬取各大音乐评论的代码
2021/04/13 Python
DBCA命令行搭建Oracle ADG的流程
2021/06/11 Oracle