1.前言
element-ui框架的select组件,点击后,会出现下拉菜单。当我们点击页面上除选择框和下拉菜单的区域时,下拉菜单会关闭,那么,element是如何实现的呢?
2.逻辑解读
翻开源码目录后,找到了clickoutside.js
。该文件就是处理鼠标是否点击相应元素外面的。之所以这么说,是因为这是个公共处理方法,除了select选择器,还有其他组件也用到的。
对于判断鼠标点击元素之外,element使用了vue自定义指令来实现。
好处就是有相应的钩子函数,从元素绑定,元素更新,元素销毁,都能够自动化处理,只需要在相应的钩子函数中写逻辑即可。
那么,点击事件时如何添加的呢?
element是为整个文档添加了鼠标按下,鼠标弹起事件。
!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e));
!Vue.prototype.$isServer && on(document, 'mouseup', e => {
nodeList.forEach(node => node[ctx].documentHandler(e, startClick));
那么,判断具体是如何实现的呢?
它在自定义指令的bind
钩子中,为元素添加了createDocumentHandler
方法。该方法是整个判断逻辑的核心。代码解释写在相应的地方了。
// 点击的话,判断两个地方,一个是ElSelect,另一个是ElSelectDropdown
function createDocumentHandler(el, binding, vnode) {
return function(mouseup = {}, mousedown = {}) {
if (!vnode ||
!vnode.context ||
!mouseup.target ||
!mousedown.target ||
// contains:判断鼠标点击的dom是否为el的后代节点
el.contains(mouseup.target) ||
el.contains(mousedown.target) ||
el === mouseup.target ||
// popperElm:是ElSelectDropdown组件DOM
// context是VNode的一个属性,其值可为component/void
(vnode.context.popperElm &&
(vnode.context.popperElm.contains(mouseup.target) ||
vnode.context.popperElm.contains(mousedown.target)))) return;
if (binding.expression &&
el[ctx].methodName &&
vnode.context[el[ctx].methodName]) {
console.log('外面点击');
vnode.context[el[ctx].methodName]();
} else {
el[ctx].bindingFn && el[ctx].bindingFn();
}
};
}
要判断就是两个地方,一个选择框,一个下拉菜单。如果都不是,那么就关闭下拉菜单。这里用了Node.contains()
api,判断传入节点是否为该节点的后代节点。下面是MDN解释:
需要特别注意:鼠标点击和弹起的地方(弹起和点击的地方一定要相同,这里是非常需要注意的),不是选择框和下拉菜单区域的话,就关闭下拉菜单。
- 点击区域是否为选择框
el.contains(mouseup.target) ||
el.contains(mousedown.target) ||
el === mouseup.target ||
- 是否为下拉菜单区域
(vnode.context.popperElm &&
(vnode.context.popperElm.contains(mouseup.target) ||
vnode.context.popperElm.contains(mousedown.target))))