一个因@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 相关文章推荐
JS中判断null、undefined与NaN的方法
Mar 26 Javascript
javascript中的作用域和闭包详解
Jan 13 Javascript
js实现input密码框提示信息的方法(附html5实现方法)
Jan 14 Javascript
JS实现京东首页之页面顶部、Logo和搜索框功能
Jan 12 Javascript
关于Angular2 + node接口调试的解决方案
May 28 Javascript
关于使用axios的一些心得技巧分享
Jul 02 Javascript
AngularJS实现进度条功能示例
Jul 05 Javascript
微信小程序视图template模板引用的实例详解
Sep 20 Javascript
jquery实现联想词搜索框和搜索结果分页的示例
Oct 10 jQuery
详解无限滚动插件vue-infinite-scroll源码解析
May 12 Javascript
深入浅析JavaScript中的in关键字和for-in循环
Apr 20 Javascript
html实现随机点名器的示例代码
Apr 02 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中序列化与反序列化详解
2017/02/13 PHP
PHP获取真实IP及IP模拟方法解析
2020/11/24 PHP
jquery.artwl.thickbox.js  一个非常简单好用的jQuery弹出层插件
2012/03/01 Javascript
jQuery实现切换页面过渡动画效果
2015/10/29 Javascript
js判断复选框是否选中及选中个数的实现代码
2016/05/30 Javascript
ros::spin() 和 ros::spinOnce()函数的区别及详解
2016/10/01 Javascript
Dropzone.js实现文件拖拽上传功能(附源码下载)
2016/11/22 Javascript
解析ajaxFileUpload 异步上传文件简单使用
2016/12/30 Javascript
基于cookie实现zTree树刷新后展开状态不变
2017/02/28 Javascript
基于vue+canvas的excel-like组件实例详解
2017/11/28 Javascript
解决Vue+Element ui开发中碰到的IE问题
2018/09/03 Javascript
vue计算属性get和set用法示例
2019/02/08 Javascript
通过JQuery,JQueryUI和Jsplumb实现拖拽模块
2019/06/18 jQuery
如何用vue-cli3脚手架搭建一个基于ts的基础脚手架的方法
2019/12/12 Javascript
基于Element封装一个表格组件tableList的使用方法
2020/06/29 Javascript
[01:18]一目了然!DOTA2DotA快捷操作对比第一弹
2014/07/01 DOTA
跟老齐学Python之总结参数的传递
2014/10/10 Python
python使用in操作符时元组和数组的区别分析
2015/05/19 Python
Python爬取APP下载链接的实现方法
2016/09/30 Python
Python 正则表达式入门(中级篇)
2016/12/07 Python
python去除空格和换行符的实现方法(推荐)
2017/01/04 Python
Python实现爬取马云的微博功能示例
2019/02/16 Python
python os.path.isfile 的使用误区详解
2019/11/29 Python
html5 worker 实例(一) 为什么测试不到效果
2013/06/24 HTML / CSS
英国品牌男装折扣网站:Brown Bag
2018/03/08 全球购物
ZWILLING双立人英国网上商店:德国刀具锅具厨具品牌
2018/05/15 全球购物
澳大利亚人信任的清洁平台,您的私人管家:Jarvis
2020/12/25 全球购物
应用心理学个人的求职信
2013/12/08 职场文书
《湘夫人》教学反思
2014/02/21 职场文书
拉歌口号大全
2014/06/13 职场文书
刑事和解协议书范本
2014/11/19 职场文书
2016应届毕业生实习评语
2015/12/01 职场文书
php+laravel 扫码二维码签到功能
2021/05/15 PHP
Java中常用解析工具jackson及fastjson的使用
2021/06/28 Java/Android
Java 获取Word中所有的插入和删除修订的方法
2022/04/06 Java/Android
vue实力踩坑之push当前页无效
2022/04/10 Vue.js