一个因@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 相关文章推荐
JSON 数据格式介绍
Jan 13 Javascript
Jquery读取URL参数小例子
Aug 30 Javascript
常用的jquery模板插件——jQuery Boilerplate介绍
Sep 23 Javascript
javascript框架设计之框架分类及主要功能
Jun 23 Javascript
JavaScript子窗口调用父窗口变量和函数的方法
Oct 09 Javascript
Bootstrap每天必学之导航条
Nov 27 Javascript
jQuery日历插件datepicker用法详解
Mar 03 Javascript
bootstrap组件之按钮式下拉菜单小结
Jan 19 Javascript
Vue v2.5 调整和更新不完全问题
Oct 24 Javascript
详解在不使用ssr的情况下解决Vue单页面SEO问题
Nov 08 Javascript
微信小程序scroll-view点击项自动居中效果的实现
Mar 25 Javascript
vue 如何使用递归组件
Oct 23 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/08/19 PHP
PHP利用MySQL保存session的实现思路及示例代码
2014/09/09 PHP
PHP使用MPDF类生成PDF的方法
2015/12/08 PHP
laravel withCount 统计关联数量的方法
2019/10/10 PHP
javascript高级学习笔记整理
2011/08/14 Javascript
ie下动态加态js文件的方法
2011/09/13 Javascript
jQuery源码分析-02正则表达式 RegExp 常用正则表达式
2011/11/14 Javascript
JavaScript基础语法让人疑惑的地方小结
2012/05/23 Javascript
jQuery实现table隔行换色和鼠标经过变色的两种方法
2014/06/15 Javascript
jQuery对指定元素中指定字符串进行替换的方法
2015/03/17 Javascript
基于jquery实现图片上传本地预览功能
2016/01/08 Javascript
关于JavaScript作用域你想知道的一切
2016/02/04 Javascript
基于JS实现省市联动效果代码分享
2016/06/06 Javascript
AngularJS入门教程之链接与图片模板详解
2016/08/19 Javascript
Windows环境下npm install 报错: operation not permitted, rename的解决方法
2016/09/26 Javascript
angular ng-repeat数组中的数组实例
2017/02/18 Javascript
详解基于Bootstrap+angular的一个豆瓣电影app
2017/06/26 Javascript
详解VueRouter进阶之导航钩子和路由元信息
2017/09/13 Javascript
详解vue-meta如何让你更优雅的管理头部标签
2018/01/18 Javascript
JavaScript ES6箭头函数使用指南
2018/12/30 Javascript
jQuery实现的网站banner图片无缝轮播效果完整实例
2019/01/28 jQuery
js简单的分页器插件代码实例
2019/09/11 Javascript
JS绘图Flot如何实现动态可刷新曲线图
2020/10/16 Javascript
在Python中关于中文编码问题的处理建议
2015/04/08 Python
浅谈django的render函数的参数问题
2018/10/16 Python
详解爬虫被封的问题
2019/04/23 Python
详解CSS透明opacity和IE各版本透明度滤镜filter的最准确用法
2016/12/20 HTML / CSS
使用HTML5的表单验证的简单示例
2015/09/09 HTML / CSS
高级人员简历的自我评价分享
2013/11/03 职场文书
趣味运动会广播稿
2014/09/13 职场文书
勿忘国耻9.18演讲稿(经典篇)
2014/09/14 职场文书
个人反四风对照检查材料思想汇报
2014/09/23 职场文书
成品仓管员岗位职责
2015/04/01 职场文书
少年犯观后感
2015/06/11 职场文书
MySQL修改默认引擎和字符集详情
2021/09/25 MySQL
Java练习之潜艇小游戏的实现
2022/03/16 Java/Android