一个因@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 相关文章推荐
网站404页面3秒后跳到首页的实例代码
Aug 16 Javascript
jQuery验证插件 Validate详解
Nov 20 Javascript
js实现在网页上简单显示时间的方法
Mar 02 Javascript
javascript中的正则表达式使用详解
Aug 30 Javascript
js实现选中页面文字将其分享到新浪微博
Nov 05 Javascript
利用JS实现数字增长
Jul 28 Javascript
promise处理多个相互依赖的异步请求(实例讲解)
Aug 03 Javascript
JavaScript 数组的进化与性能分析
Sep 18 Javascript
React 高阶组件入门介绍
Jan 11 Javascript
基于vue--key值的特殊用处详解
Jul 31 Javascript
linux服务器快速卸载安装node环境(简单上手)
Feb 22 Javascript
Vue实现下拉加载更多
May 09 Vue.js
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函数之子字符串替换&amp;#65279; str_replace
2011/03/23 PHP
PHP源码之explode使用说明
2011/08/05 PHP
PHP中几种常见的超时处理全面总结
2012/09/11 PHP
PHP 关于访问控制的和运算符优先级介绍
2013/07/08 PHP
php+html5+ajax实现上传图片的方法
2016/05/14 PHP
PHP的RSA加密解密方法以及开发接口使用
2018/02/11 PHP
PHP7内核CGI与FastCGI详解
2019/04/14 PHP
php判断IP地址是否在多个IP段内
2020/08/18 PHP
Docker 安装 PHP并与Nginx的部署实例讲解
2021/02/27 PHP
javascript URL锚点取值方法
2009/02/25 Javascript
常见表单重复提交问题整理及解决方法
2013/11/13 Javascript
原生js实现的贪吃蛇网页版游戏完整实例
2015/05/18 Javascript
javascript中arguments,callee,caller详解
2016/03/16 Javascript
浅谈Cookie的生命周期问题
2016/08/02 Javascript
js实现做通讯录的索引滑动显示效果和滑动显示锚点效果
2017/02/18 Javascript
Puppeteer环境搭建的详细步骤
2018/09/21 Javascript
vue+php实现的微博留言功能示例
2019/03/16 Javascript
详解小程序退出页面时清除定时器
2019/04/28 Javascript
a标签调用js的方法总结
2019/09/05 Javascript
使用flow来规范javascript的变量类型
2019/09/12 Javascript
vue+elementUI(el-upload)图片压缩,默认同比例压缩操作
2020/08/10 Javascript
[02:35]DOTA2英雄基础教程 末日使者
2013/12/04 DOTA
Python bsddb模块操作Berkeley DB数据库介绍
2015/04/08 Python
Python检测字符串中是否包含某字符集合中的字符
2015/05/21 Python
Python中urllib+urllib2+cookielib模块编写爬虫实战
2016/01/20 Python
使用pycharm生成代码模板的实例
2018/05/23 Python
django+mysql的使用示例
2018/11/23 Python
Python 使用 Pillow 模块给图片添加文字水印的方法
2019/08/30 Python
python 利用jinja2模板生成html代码实例
2019/10/10 Python
Python Websocket服务端通信的使用示例
2020/02/25 Python
基于Python和C++实现删除链表的节点
2020/07/06 Python
css背景图片的背景裁切、背景透明度、背景变换等效果运用
2012/12/24 HTML / CSS
选购世界上最好的美妆品:Cult Beauty
2017/11/03 全球购物
精彩的广告词
2014/03/19 职场文书
银行主办会计岗位职责
2014/08/13 职场文书
奥特曼十大神器:奥特手镯在榜,第一是贝利亚的神器
2022/03/18 日漫