一个因@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语句中的CDATA标签的意义
May 09 Javascript
html中table数据排序的js代码
Aug 09 Javascript
解决jquery插件冲突的问题
Jan 23 Javascript
从数据库读取数据后将其输出成html标签的三种方法
Oct 13 Javascript
用js读、写、删除Cookie代码续篇
Dec 03 Javascript
JS实现完全语义化的网页选项卡效果代码
Sep 15 Javascript
jQuery动画效果相关方法实例分析
Dec 31 Javascript
JavaScript 中定义函数用 var foo = function () {} 和 function foo()区别介绍
Mar 01 Javascript
微信小程序支付PHP代码
Aug 23 Javascript
vue实现菜单切换功能
May 08 Javascript
js 数组当前行添加数据方法详解
Jul 28 Javascript
关于对TypeScript泛型参数的默认值理解
Jul 15 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教程之魔术方法的使用示例(php魔术函数)
2014/02/12 PHP
thinkPHP内置字符串截取函数用法详解
2016/11/15 PHP
复制小说文本时出现的随机乱码的去除方法
2010/09/07 Javascript
浅谈Javascript事件模拟
2012/06/27 Javascript
利用函数的惰性载入提高javascript代码执行效率
2014/05/05 Javascript
chrome下img加载对height()的影响示例探讨
2014/05/26 Javascript
JQuery中使用.each()遍历元素学习笔记
2014/11/08 Javascript
Nodejs实战心得之eventproxy模块控制并发
2015/10/27 NodeJs
jQuery+ajax实现文章点赞功能的方法
2015/12/31 Javascript
jQuery基于muipicker实现仿ios时间选择
2016/02/22 Javascript
浅谈原生JS实现jQuery的animate()动画示例
2017/03/08 Javascript
微信小程序 出现47001 data format error原因解决办法
2017/03/10 Javascript
详解react-webpack2-热模块替换[HMR]
2017/08/03 Javascript
Vue 中使用vue2-highcharts实现top功能的示例
2018/03/05 Javascript
JS实现DOM节点插入操作之子节点与兄弟节点插入操作示例
2018/07/30 Javascript
vuex actions异步修改状态的实例详解
2019/11/06 Javascript
[01:06]DOTA2小知识课堂 Ep.02 吹风竟可解梦境缠绕
2019/12/05 DOTA
在windows系统中实现python3安装lxml
2016/03/23 Python
Python之日期与时间处理模块(date和datetime)
2017/02/16 Python
Python利用itchat对微信中好友数据实现简单分析的方法
2017/11/21 Python
Python 内置函数memoryview(obj)的具体用法
2017/11/23 Python
Python实现求一个集合所有子集的示例
2018/05/04 Python
你应该知道的Python3.6、3.7、3.8新特性小结
2020/05/12 Python
解决keras加入lambda层时shape的问题
2020/06/11 Python
python在linux环境下安装skimage的示例代码
2020/10/14 Python
美国购买汽车零件网站:Buy Auto Parts
2018/04/02 全球购物
资产经营总监岗位职责范文
2013/12/01 职场文书
副总经理岗位职责范本
2014/09/30 职场文书
教师批评与自我批评总结
2014/10/16 职场文书
2014年检验科工作总结
2014/11/22 职场文书
公司内部升职自荐信
2015/03/27 职场文书
2015年学校保卫部工作总结
2015/05/11 职场文书
《鲁滨逊漂流记》之六读后感(4篇)
2019/09/29 职场文书
Nginx 502 Bad Gateway错误原因及解决方案
2021/03/31 Servers
Redis安装使用RedisJSON模块的方法
2022/03/23 Redis
微信告警的zabbix监控系统 监控整个NGINX集群
2022/04/18 Servers