el-select框在页面滚动时el-option超出元素区域

2,659 阅读3分钟

问题场景:有页面内嵌了表格(含滚动条),内嵌了大量input和select,在点击select出现下拉框时,正常的情况是图1,但是在滚动表格内的滚动条时,会出现图二的情况。

导致原因:select的下拉框的元素标签不是跟随在select之后,而是在body标签下面,并且z-index较高,如图3。

图1

图2

图3

解决办法

首先,尝试popper-append-to-body属性设置为false之后无效。

有效的解决办法来自www.e-learn.cn/topic/28212…

该文章的核心思想是,

  • 监听鼠标按下事件;
  • 当鼠标按下时,判断点击元素是不是select,如果是置lock为false;如果不是直接返回;
  • 当鼠标再次点击时,点击非select元素且lock为false,则强制执行鼠标按下、抬起事件(目的时让select下拉框收起),置lock为true;
  • 监听滚轮事件;
  • 当鼠标位置下方的元素是select,select内置滚动条,则直接返回;
  • 当鼠标位置下方的元素非select,则强制执行鼠标按下、抬起事件(目的时让select下拉框收起),置lock为true;

1. 创建js文件

// 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 => {
  const classList = e.target.classList;
  // 该判断条件是用来判断点击的是否为下拉框
  // el-select__caret代表是点击小三角出现下拉框,el-input__inner代表点击的整个input框
  // 如果公司组件的类名不同,对应修改即可
  if (
    (classList && classList.contains('el-select__caret')) ||
    (classList && classList.contains('el-input__inner'))
  ) {
    lock = false;
    return;
  }
  if (lock) return;
  fakeClickOutSide();
};
const mousewheelHandle = e => {
  // 如果当前select本身有滚动条,则直接返回
  // 原文对于classList没有判空,会报错
  if (
    lock ||
    (e.target.classList.length !== 0 &&
      e.target.classList.contains('el-select-dropdown__item')) ||
    (e.target.parentNode.classList !== undefined &&
      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;
  }
};

2. 使用

在想要引入的组件内

import fackClickOutSide from '@/mixin/fackClickOutSide.js';
export default {
  name: 'FormWithAnchor',
  mixins: [fackClickOutSide],
}

3.知识点

1. Event

Event()构造函数创建一个新的Event。

event = new Event(typeArg,eventInit);

typeArg:事件名称;

eventInit:这是一个对象,包含以下字段:

  • bubbles:(可选)Boolean指示事件是否冒泡。默认是false
  • cancelable:(可选)a Boolean表示是否可以取消该事件。默认是false

2. document.dispatch

dispatchEvent() 就是触发执行,

dom.dispatchEvent(eventObject),参数eventObject 表示事件对象,是createEvent()方法返回的创建的Event对象。

作用:触发执行。例如:鼠标未按下,触发鼠标按下的event函数,能达到和鼠标按下一样的效果。

3. DOM addEventListener添加事件监听函数

element.addEventListener(event, function, useCapture)

event:必须。字符串,指定事件名。

function: 必须。指定要事件触发时执行的函数。

useCapture:可选。布尔值,指定事件是否在捕获或冒泡阶段执行。可能值:

  • true:事件句柄在捕获阶段执行
  • false-:默认。事件句柄在冒泡阶段执行

4. e.target

谁触发事件代表的就是谁。

target 属性规定哪个 DOM 元素触发了该事件; target 事件属性可返回事件的目标节点(触发该事件的节点),如生成事件的元素、文档或窗口。