一个因@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 相关文章推荐
HTML中Select不用Disabled实现ReadOnly的效果
Apr 07 Javascript
地震发生中逃生十大法则
May 12 Javascript
js 屏蔽鼠标右键脚本附破解方法
Dec 03 Javascript
避免 showModalDialog 弹出新窗体的原因分析
May 31 Javascript
jquery 添加节点的几种方法介绍
Sep 04 Javascript
jquery解析xml字符串简单示例
Apr 11 Javascript
鼠标移到图片上变大显示而不是放大镜效果
Jun 15 Javascript
js实现刷新iframe的方法汇总
Apr 27 Javascript
JavaScript观察者模式(经典)
Dec 09 Javascript
AngularJS入门教程之表单校验用法示例
Nov 02 Javascript
详解node child_process模块学习笔记
Jan 24 Javascript
vue设置默认首页的操作
Aug 12 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验证码实现代码(3种)
2015/09/07 PHP
php将服务端的文件读出来显示在web页面实例
2016/10/31 PHP
使用Codeigniter重写insert的方法(推荐)
2017/03/23 PHP
php大小写转换函数(strtolower、strtoupper)用法介绍
2017/11/17 PHP
php实现微信原生支付(扫码支付)功能
2018/05/30 PHP
如何在centos8自定义目录安装php7.3
2019/11/28 PHP
js 中{},[]中括号,大括号使用详解
2011/05/12 Javascript
js文件缓存之版本管理详解
2013/07/05 Javascript
常用的JavaScript验证正则表达式汇总
2013/11/26 Javascript
关于页面嵌入swf覆盖div层的问题的解决方法
2014/02/11 Javascript
jquery日历控件实现方法分享
2014/03/07 Javascript
jquery实现浮动在网页右下角的彩票开奖公告窗口代码
2015/09/04 Javascript
10个很棒的jQuery代码片段
2015/09/24 Javascript
jQuery插入节点和移动节点用法示例(insertAfter、insertBefore方法)
2016/09/08 Javascript
微信小程序中的onLoad详解及简单实例
2017/04/05 Javascript
微信小程序多列选择器range-key使用详解
2020/03/30 Javascript
动态加载JavaScript文件的3种方式
2018/05/05 Javascript
django使用channels2.x实现实时通讯
2018/11/28 Javascript
解决layui的input独占一行的问题
2019/09/10 Javascript
[06:37]2014DOTA2国际邀请赛 昔日王者渴望重回巅峰
2014/07/12 DOTA
Python中使用Boolean操作符做真值测试实例
2015/01/30 Python
Python实现删除当前目录下除当前脚本以外的文件和文件夹实例
2015/07/27 Python
Python编程实现生成特定范围内不重复多个随机数的2种方法
2017/04/14 Python
Python3实战之爬虫抓取网易云音乐的热门评论
2017/10/09 Python
Java及python正则表达式详解
2017/12/27 Python
Python使用add_subplot与subplot画子图操作示例
2018/06/01 Python
Python实现的连接mssql数据库操作示例
2018/08/17 Python
纯css3实现的动画按钮的实例教程
2014/11/17 HTML / CSS
美国知名的网上鞋类及相关服装零售商:Shoes.com
2017/05/06 全球购物
大学班长的职责
2014/01/27 职场文书
驾驶员培训方案
2014/05/01 职场文书
努力学习演讲稿
2014/05/10 职场文书
小学语文复习计划
2015/01/19 职场文书
2016读书月活动心得体会
2016/01/14 职场文书
python利用while求100内的整数和方式
2021/11/07 Python
java高级用法JNA强大的Memory和Pointer
2022/04/19 Java/Android