一个因@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 相关文章推荐
web 页面分页打印的实现
Jun 22 Javascript
面向对象的javascript(笔记)
Oct 06 Javascript
JavaScript常用脚本汇总(一)
Mar 04 Javascript
JavaScript常用脚本汇总(二)
Mar 04 Javascript
JavaScript获取表单内所有元素值的方法
Apr 02 Javascript
js实现的彩色方块飞舞奇幻效果
Jan 27 Javascript
angular 动态组件类型详解(四种组件类型)
Feb 22 Javascript
利用Vue.js实现求职在线之职位查询功能
Jul 03 Javascript
关于js的三种使用方式(行内js、内部js、外部js)的程序代码
May 05 Javascript
vue-swiper的使用教程
Aug 30 Javascript
uni-app之APP和小程序微信授权方法
May 09 Javascript
vue中keep-alive组件的入门使用教程
Jun 06 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
4.与数据库的连接
2006/10/09 PHP
PHP错误抑制符(@)导致引用传参失败Bug的分析
2011/05/02 PHP
win7+apache+php+mysql环境配置操作详解
2013/06/10 PHP
php使用ffmpeg获取视频信息并截图的实现方法
2016/05/03 PHP
JQuery jsonp 使用示例代码
2009/08/12 Javascript
jQuery的链式调用浅析
2010/12/03 Javascript
JS基础之undefined与null的区别分析
2011/08/08 Javascript
ASP.NET jQuery 实例5 (显示CheckBoxList成员选中的内容)
2012/01/13 Javascript
JavaScript 32位整型无符号操作示例
2013/12/08 Javascript
禁用页面部分JavaScript不是全部而是部分
2014/09/03 Javascript
纯javascript代码实现计算器功能(三种方法)
2015/09/07 Javascript
AngularJS框架的ng-app指令与自动加载实现方法分析
2017/01/04 Javascript
jquery事件与绑定事件
2017/03/16 Javascript
Echarts基本用法_动力节点Java学院整理
2017/08/11 Javascript
vue绑定设置属性的多种方式(5)
2017/08/16 Javascript
解决微信二次分享不显示摘要和图片的问题
2017/08/18 Javascript
vue路由对不同界面进行传参及跳转的总结
2019/04/20 Javascript
javascript中的数据类型检测方法详解
2019/08/07 Javascript
微信小程序入门之绘制时钟
2020/10/22 Javascript
[01:42]辉夜杯战队访谈宣传片—FANTUAN
2015/12/25 DOTA
Python递归遍历列表及输出的实现方法
2015/05/19 Python
Python中在脚本中引用其他文件函数的实现方法
2016/06/23 Python
wxpython实现图书管理系统
2018/03/12 Python
pygame游戏之旅 添加游戏界面按键图形
2018/11/20 Python
Python多进程写入同一文件的方法
2019/01/14 Python
Python logging模块进行封装实现原理解析
2020/08/07 Python
学生如何注册Pycharm专业版以及pycharm的安装
2020/09/24 Python
Python jieba结巴分词原理及用法解析
2020/11/05 Python
JavaScript+Canvas实现自定义画板的示例代码
2019/05/13 HTML / CSS
瑰珀翠美国官网:Crabtree & Evelyn美国
2016/11/29 全球购物
生日寄语大全
2014/04/08 职场文书
捐献物资倡议书范文
2014/05/19 职场文书
交通事故和解协议书
2014/09/25 职场文书
2019财务管理制度最新范本!
2019/07/09 职场文书
vue中data改变后让视图同步更新的方法
2021/03/29 Vue.js
vue @ ~ 相对路径 路径别名设置方式
2022/06/05 Vue.js