一个因@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 相关文章推荐
javascript 写类方式之六
Jul 05 Javascript
使用javascript获取flash加载的百分比的实现代码
May 25 Javascript
CodeMirror2 IE7/IE8 下面未知运行时错误的解决方法
Mar 29 Javascript
用javascript判断IE版本号简单实用且向后兼容
Sep 11 Javascript
千分位数字格式化(用逗号隔开 代码已做了修改 支持0-9位逗号隔开)的JS代码
Dec 05 Javascript
jQuery实现checkbox全选的方法
Jun 10 Javascript
js生成随机数(指定范围)的实例代码
Jul 10 Javascript
javascript数组去重方法分析
Dec 15 Javascript
vue 右键菜单插件 简单、可扩展、样式自定义的右键菜单
Nov 29 Javascript
详解vue-cli3多环境打包配置
Mar 28 Javascript
JS实现指定区域的全屏显示功能示例
Apr 25 Javascript
使用Vue开发自己的Chrome扩展程序过程详解
Jun 21 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
phpmyadmin中配置文件现在需要绝密的短语密码的解决方法
2007/02/11 PHP
PHP 面向对象详解
2012/09/13 PHP
PHP根据IP地址获取所在城市具体实现
2013/11/27 PHP
PHP支付宝当面付2.0代码
2018/12/21 PHP
JavaScript的parseInt 进制问题
2009/05/07 Javascript
js 时间函数应用加、减、比较、格式转换的示例代码
2013/08/23 Javascript
javascript实现json页面分页实例代码
2014/02/20 Javascript
浅谈重写window对象的方法
2014/12/29 Javascript
JS通过ajax动态读取xml文件内容的方法
2015/03/24 Javascript
jQuery使用fadeout实现元素渐隐效果的方法
2015/03/27 Javascript
浅析JavaScript访问对象属性和方法及区别
2015/11/16 Javascript
轻松实现js图片预览功能
2016/01/18 Javascript
jQuery实现圣诞节礼物动画案例解析
2016/12/25 Javascript
JS调用Android、Ios原生控件
2017/01/06 Javascript
JavaScript之DOM插入更新删除_动力节点Java学院整理
2017/07/03 Javascript
AngularJS监听ng-repeat渲染完成的方法
2018/03/20 Javascript
vue生命周期实例小结
2018/08/15 Javascript
jQuery实现的点击显示隐藏下拉菜单功能完整示例
2019/05/17 jQuery
json 带斜杠时如何解析的实现
2019/08/12 Javascript
[00:30]塑造者的传承礼包-戴泽“暗影之焰”套装展示视频
2014/04/04 DOTA
[01:34]2014DOTA2展望TI 剑指西雅图VG战队专访
2014/06/30 DOTA
Python实现PS图像调整之对比度调整功能示例
2018/01/26 Python
Python 12306抢火车票脚本
2018/02/07 Python
python调用摄像头显示图像的实例
2018/08/03 Python
Python实现将字符串的首字母变为大写,其余都变为小写的方法
2019/06/11 Python
python实现猜数游戏(保存游戏记录)
2020/06/22 Python
深入浅析css3 中display box使用方法
2015/11/25 HTML / CSS
澳大利亚领先的在线美容商店:Facial Co
2017/10/22 全球购物
阿迪达斯荷兰官方网站:adidas荷兰
2018/03/16 全球购物
党员政治学习材料
2014/05/14 职场文书
企业口号大全
2014/06/12 职场文书
计算机毕业大学生求职信
2014/06/26 职场文书
公务员年度个人总结
2015/02/12 职场文书
乔迁新居祝福语
2019/11/04 职场文书
《分一些蚊子进来》读后感3篇
2020/01/09 职场文书
SqlServer 垂直分表(减少程序改动)
2021/04/16 SQL Server