48 div 下面包含 el-radio, 点击 div 事件被触发多次

前言

这是一个最近碰到的一个很奇怪的问题 

情况如下一个 div 下面有一个 el-radio, 然后 div 上面配置了 click 的回调为 handleClick

然后 但是点击 div 的时候, handleClick 触发了两次 

然后 这里 来模拟一下, 并解决一下 这个问题

这里的知识主要是 设计到 label 和 表单元素 的联动

 

 

测试用例

包了多组 div 下面的多组 el-radio, 然后 这时候点击 选项1, 选项2, 选项3 会发现 handleClick 触发了两次 

<template>
  <div class="testParent" >
    <div class="radioParent" v-for="item in selectList" @click="handleClick($event, item)" style="float: left; ">
      <el-radio v-model="checkItem" :label="item.name"> {{item.name}} </el-radio>
    </div>
  </div>
</template>

<script>
  export default {
    name: 'App',
    components: {
    },
    data() {
      return {
        checkItem: '',
        selectList: [
          {code: 'opt1', name: '选项1' },
          {code: 'opt2', name: '选项2' },
          {code: 'opt3', name: '选项3' }
        ]
      };
    },
    computed: {},
    created() {
    },
    methods: {
      handleClick($event, item) {
        console.log(' clicked item ', $event, item);
      }
    }
  };
</script>

<style>
  .radioParent {
    cursor: pointer;
    width: 80px;
    height: 32px;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #ffffff;
    border-radius: 2px;
    border: solid 1px #d9dadb;
    color: #8c9094;
    font-size: 16px;
    margin-right: 13px;
  }

  .radioParent .el-radio {
    height: 32px !important;
    line-height: 32px !important;
  }
  .radioParent .el-radio__input {
    height: 32px !important;
    line-height: 32px !important;
  }
</style>

 

 

点击一下 选项2 结果如下 

可以看到 handleClick 的回调触发了两次

0dffb26ee64f4886930db39495a8f9e7.png

 

 

el-radio 中为什么点击 span 也能选中目标?

这是 我很疑惑的一个点, 然后 点开了 element-ui 的源码 编译了一下

然后 测试用例 复制进去, 跑起来 调试一下

然后 这里的 input 的 change 触发了, 但是 不知道 是哪里触发的

我这里点击的是 span[其内容是”选项2”], 但是事件传递到了 input 这里 

数据视图切换的标准是 input 框的 change 触发, 造成了 model 的更新, 才会进行切换 

9d6a2c68d13143ad8346a6e4470fba4a.png

 

然后开始 其他的调试

radioParent 的 div 的 事件处理列表 如下, 然后 其子元素也继承了这一套 事件处理列表

b78c7ce01d6d47798db4cbd351c2856e.png

 

然后 我们把 radioParent 的 div 的 事件处理列表 删除掉 

然后点击查看 radioParent 以及下面的各个子元素的 事件处理列表, 可以看到 除了 input框 还有 focus, blur, change 的 事件处理列表

e798707948274690a15262079e8d10d3.png

 

input 上面的事件如下 

a00dce0c81544bf0b9efea1f7c89e191.png

 

然后 此时再从 “其他选项” 点击到 “选项3” 的时候, 可以看到 这个 click 还是可以正常响应 

然后 这里可以判断出的就是 这个 input 的 change 事件 不是 span 传递过来的 

5ac5cc618ec34b6694f1517d0a6d4a95.png

 

 

label + input 标签的联动 – 修改 label

然后 最终搜索了一下 发现是 label + input 标签的联动 

当点击了 label 标签范围内的数据, 会自动视为点击了 input 标签, 提升了用户体验 

为了验证这个问题, 我们将 “选项3” 的 label 标签换成 span, 初始化状态 选中 “选项2”

 

点击 span “选项3” 可以看到的现象是 hanleClick 触发了一次, 但是 视图模型并没有切换过去, 说明 input框 没有选中, 但是在有 label 标签的时候点击了 label 进而使 input框选中了

0fcf2ee9efbe44488b7480bd64b6ec30.png

 

这里实际点击的元素是 el-radio__label 的 span

33d53cae305f44f29f8557d89209531c.png

 

点击 input 框, 可以看到的现象是 hanleClick 触发了一次, 但是 视图模型并没有切换过去 

说明 input框 没有选中, 但是在有 label 标签的时候点击了 label 进而使 input框选中了

840ea3ea698a4dad9777aab2f2a0cdc4.png

 

这里实际点击的元素是 el-radio__inner 的 span

d775e9d4ebf04825a2930affbd83e7e0.png

 

上面的点击 span 的时候 input 框没有被选中的道理很好理解, 但是 后者呢? 为什么选中了 input 框, 这时候点击 还是没有触发 input 框的更新 

这是因为 element-ui 配置的 z-index

input 框的 .el-radio__original 设置的 z-index 是 -1, el-radio__inner 的 span 的 z-index 是 auto, 在外层父元素的 z-index 也是 auto, 所以 点击到 按钮位置 实际上点击到的是 span 

这个也可以通过 上面的鼠标事件的 target 来进行判断 

84f7454006d043ed82ca7090a5ee6b62.png

 

“选项3” 的 input 设置一个 z-index 1000, 然后 点击看一下, 这之后 点击 input 元素 就能够正常选中了

55248c789c9245edb55ebba3d652c9c4.png

 

看一下 事件点击的元素 就是目标 input 框了 

348d546b07c742e8bafd70067941ba77.png

 

 

label + input 标签的联动 – 修改 input

修改 el-radio 的代码, 将 input 修改为 span, 然后 我们来查看一下 效果

62e645f205af42148ca23ea8a03f4018.png

点击一个选择框, 可以看到 handleClick 只会触发一次了 

同时 也因为缺少了 input 框, 导致没有触发 input 的 change, 导致 checkItem 未产生变化 

c817e36f87a4469696adaa4cae4d03fc.png

 

 

问题的调试

上面的这一个章节 只是一个基础的补充

然后 我们这里来看一下 我们这里用例的问题, 为什么 点击了一次 触发了 两次 click 函数 

第一个事件触发是点击的元素, 比如我们这里点击 label, 那就是 el-radio__label 的 span 元素, 如果我们这里点击的是 input 的位置, 那就是 el-radio__inner 的 span 

这里我们点击目标元素, 目标元素

4b152c46f8f440a982d390e979bbd61f.png

 

然后第二次事件触发的是 input 元素, 可以发现 不管点击的是 div 的哪一个区域, 第二次的 target 都是 input 元素 

这个 通过上面一个章节 的基础的补充, 应该还是 能够比较快的猜到是 label 的点击 然后使得事件传递到了 input 框 

6d4e26ccfbba4b8aae2d1692f6c39e64.png

 

 

问题的解决

  1. 这时候, 在这个场景下面 通常来说的处理方式 就是, 拿到 事件event, 然后根据 target 进行一个过滤, 比如 只处理 input 的这一个事件, 来保证业务的准确 
  2. 注册事件的时候注册为 click.prevent, 这样会阻止事件的默认行为, 比如这里的点击了 label 使得 input 收到同样的事件, 所以只会有第一个点击目标元素的事件 
  3. 不使用 el-radio, 这个实现是固定的, 无法改变, 使用其他的替代方案 

 

 

 

 

 

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值