一个因@click.stop引发的bug的解决


Posted in Javascript onJanuary 08, 2019

问题

在项目页面中使用 element popover,设置trigger='click'时点击外部不会触发自动隐藏,但在 element 官网中是可以正常触发的(官方示例),项目中的菜单是自定义写的,所以怀疑是有黑魔法。

查找原因

  1. 将 popover 写在app.vue根组件内,发现可以正常触发自动隐藏。
  2. 在app.vue的 mounted 钩子中加入window.addEventListener('click', () => console.log('window click===>>>>')),发现只有菜单栏外层能够触发。
  3. 检查菜单栏组件,发现代码中<div class="main" @click.stop="isShowWhole = false">,这里的 click 事件使用了 stop 修饰符(阻止冒泡),可能阻止了 popover 外部点击的事件判断,尝试将 stop 修饰符去掉,发现外部点击事件正常触发。

确认代码修改没有副作用

在修复 bug 时,需要注意不会产生额外的 bug,那就需要了解修改的这段代码的含义

@click.stop="isShowWhole = false"

从代码上看,点击 class 为 main 的 div 将会触发左边侧边栏缩略显示,加上 stop 修饰符是为了防止事件冒泡,所以能否去掉 stop 需要确认是否有这个必要。

// router.js
let routes = [
  {
   path: '/',
   alias: '/admin',
   component: Menu,
   children: [...Pages],
  },
  {
   path: '*',
   name: '404',
   component: NotFound,
  },
 ];

在路由中可以看到,Menu 是作为根路由进行渲染,除了 404 页面都是它的子路由,所以 stop 修饰符是没有必要加上的,去除后经过测试没有其他影响。

深入 element popover 源码分析原因

对 element 组件进行 debug 时,可以直接引入相关组件的源码

import ElPopover from 'element-ui/packages/popover';
export default {
  components: {
    CheckboxFilter,
    ElPopover
  },
  ...
}

然后我们就可以在node_modules的 element 源码进行 debug 操作(危险步骤,debug 后需要复原)。

// node_modules/element-ui/packages/popover/src/main.vue
mounted() {
  ...
  if (this.trigger === 'click') {
   on(reference, 'click', this.doToggle);
   on(document, 'click', this.handleDocumentClick);
  } else if (this.trigger === 'hover') {
   ...
  } else if (this.trigger === 'focus') {
   ...
  }
}

popover 在 mounted 钩子内初始化了trigger='click'的事件绑定,on(document, 'click', this.handleDocumentClick)这里绑定了 document 很可能就是阻止事件冒泡后不能触发外部点击隐藏的判断逻辑。

// node_modules/element-ui/packages/popover/src/main.vue
handleDocumentClick(e) {
 let reference = this.reference || this.$refs.reference;
 const popper = this.popper || this.$refs.popper;

 if (!reference && this.$slots.reference && this.$slots.reference[0]) {
  reference = this.referenceElm = this.$slots.reference[0].elm;
 }
 if (!this.$el ||
  !reference ||
  this.$el.contains(e.target) ||
  reference.contains(e.target) ||
  !popper ||
  popper.contains(e.target)) return;
 this.showPopper = false;
},

这里判断this.$el是否包含 click 的 target,从而是否触发this.showPopper = false,当菜单栏阻止事件冒泡后 document 不能监听到 click 事件,才会无法进行外部点击隐藏的判断逻辑。

延伸v-clickoutside

element 的 select 组件中用到了 v-clickoutside 自定义指令,作用和 popover 的 handleDocumentClick 差不多(倒不如说 handleDocumentClick 是特殊的 clickoutside)

在上面的问题中,我们单独把 v-clickoutside 抽出来使用确实可以的,这是为什么呢?

// node_modules/element-ui/packages/popover/src/utils/clickoutside.js
!Vue.prototype.$isServer && on(document, 'mousedown', e => (startClick = e));

!Vue.prototype.$isServer && on(document, 'mouseup', e => {
 nodeList.forEach(node => node[ctx].documentHandler(e, startClick));
});

答案是 v-clickoutside 使用鼠标事件判断的,所以 click 的 阻止冒泡不会让 clickoutside 无效。

总结

解决 bug 的过程中需要做到不产生额外的 bug,并且深入分析问题的原因有助于能力的提高。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js获取控件位置以及不同浏览器中的差别介绍
Aug 08 Javascript
html页面显示年月日时分秒和星期几的两种方式
Aug 20 Javascript
js闭包实例汇总
Nov 09 Javascript
JavaScript获取DOM元素的11种方法总结
Apr 25 Javascript
JS之获取样式的简单实现方法(推荐)
Sep 13 Javascript
微信小程序 高德地图SDK详解及简单实例(源码下载)
Jan 11 Javascript
js仿微博动态栏功能
Feb 22 Javascript
利用vue和element-ui设置表格内容分页的实例
Mar 02 Javascript
Vue项目部署在Spring Boot出现页面空白问题的解决方案
Nov 26 Javascript
SSM+layUI 根据登录信息显示不同的页面方法
Sep 20 Javascript
Javascript实现鼠标点击冒泡特效
Dec 24 Javascript
js实现消灭星星(web简易版)
Mar 24 Javascript
JavaScript学习笔记之图片库案例分析
Jan 08 #Javascript
JavaScript学习笔记之DOM操作实例分析
Jan 08 #Javascript
vue单文件组件lint error自动fix与styleLint报错自动fix详解
Jan 08 #Javascript
说说如何在Vue.js中实现数字输入组件的方法
Jan 08 #Javascript
小试SVG之新手小白入门教程
Jan 08 #Javascript
vue组件通信传值操作示例
Jan 08 #Javascript
利用d3.js力导布局绘制资源拓扑图实例教程
Jan 08 #Javascript
You might like
PHP数组遍历知识汇总(包含遍历方法、数组指针操作函数、数组遍历测速)
2014/07/05 PHP
php创建session的方法实例详解
2015/01/27 PHP
学习php设计模式 php实现装饰器模式(decorator)
2015/12/07 PHP
分析PHP中单双引号的误区和双引号小隐患
2016/07/19 PHP
Yii框架中jquery表单验证插件用法示例
2016/10/18 PHP
利用php实现一周之内自动登录存储机制(cookie、session、localStorage)
2016/10/31 PHP
[HTML/CSS/Javascript]WWTJS
2007/09/25 Javascript
HTML页面,测试JS对C函数的调用简单实例
2016/08/09 Javascript
Bootstrap基本组件学习笔记之input输入框组(9)
2016/12/07 Javascript
微信小程序中使用ECharts 异步加载数据实现图表功能
2018/07/13 Javascript
使用electron将vue-cli项目打包成exe的方法
2018/09/29 Javascript
js控制随机数生成概率代码实例
2019/03/21 Javascript
JavaScript JSON数据处理全集(小结)
2019/08/15 Javascript
vue实现评论列表功能
2019/10/25 Javascript
Python获取当前时间的方法
2014/01/14 Python
Python常用正则表达式符号浅析
2014/08/13 Python
Python的SQLalchemy模块连接与操作MySQL的基础示例
2016/07/11 Python
python中使用正则表达式的连接符示例代码
2017/10/10 Python
对python中raw_input()和input()的用法详解
2018/04/22 Python
python使用xlrd模块读取xlsx文件中的ip方法
2019/01/11 Python
基于h5py的使用及数据封装代码
2019/12/26 Python
使用python接受tgam的脑波数据实例
2020/04/09 Python
Python基于正则表达式实现计算器功能
2020/07/13 Python
HTML5 Canvas旋转动画的2个代码例子(一个旋转的太极图效果)
2014/04/10 HTML / CSS
德国鞋子网上商店:Omoda.de
2017/03/31 全球购物
C#笔试题和英文面试题
2013/02/07 面试题
中学生学雷锋演讲稿
2014/04/26 职场文书
城管大队整治方案
2014/05/06 职场文书
勤俭节约演讲稿
2014/05/08 职场文书
村安全生产责任书
2014/08/25 职场文书
暑假安全教育广播稿
2014/09/10 职场文书
暑假社会实践证明格式
2014/10/28 职场文书
2014年学生会干事工作总结
2014/11/07 职场文书
介绍信范文大全
2015/05/07 职场文书
CSS3点击按钮圆形进度打钩效果的实现代码
2021/03/30 HTML / CSS
Go语言怎么使用变长参数函数
2022/07/15 Golang