Vue3学习:汇率计算器案例中event.target与event.currentTarget比较

今天从一本vue.js书中学习了《汇率计算器》的案例,这个案例的效果如下:

案例可以查询人民币、日元、港元、美元、欧元之间的汇率关系,代码中定义了一个汇率表rate,包含了每种货币对其他5种货币的汇率。其中还有一个功能是点击下方四种货币的任意一种货币,可以和最上面的一种货币实现互换。例如点击了欧元,欧元会到最上面,和人民币位置互换。

这是代码的html部分,下面4种货币使用v-for循环生成列表项<li>。

html 复制代码
<div id="app">
    <h2 class="title">汇率计算器</h2> 
    <ul>
      <li>
        <span>{{from.currency}}</span>
        <input v-model="from.amount"></input>
      </li>
      <li v-for="item in toList"
          :data-currency="item.currency"
          @click="changeCur">
        <span>{{item.currency}}</span>
        <span>{{item.amount}}</span>
      </li> 
    </ul> 
    <p class="intro">鼠标点击可以切换货币种类</p>
  </div>

两种货币位置互换的功能,是在methods中定义了一个 changeCur(event) 方法来实现的,如下:

javascript 复制代码
      methods: {
        changeCur(event){
          const c = event.currentTarget.dataset.currency;  //获取点击的项
          const f = this.from.currency;     //获取from项
          this.from.currency = c;          //点击项赋值给from
          this.toList.find(arritem => arritem.currency === c).currency = f;  //from项赋值给点击项
        },
        exchange(from, amount, to){
          return (amount * rate[from][to]).toFixed(2)
        },
      },

给我造成困扰的是event.currentTarget,我以前在javascript中学习过e.target和this的区别,e.target指触发事件的元素,this指绑定事件的元素,那这里出现的currentTarget又是怎么回事?

资料上说,target指的是触发事件的元素,currentTarget是指监听事件的元素。为了理解这句话的含义,我还写了一段代码来验证。设置了内外两个div,为了便于区分,给内外两个div加了不同的背景色。

测试案例完整代码如下。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>target与currentTarget</title>
    <style>
        #outer {
            width: 200px;
            height: 200px;
            background-color: #339f63;
        }
        #inner {
            width: 120px;
            height: 120px;
            background-color: #23c6d9;
        }
    </style>
</head>
<body>
    <div id="outer">
        <div id="inner"></div>
    </div>
</body>
<script>
    var a = document.getElementById('outer')
    var b = document.getElementById('inner')
    function handler(e) {
        console.log(e.target);  //触发事件的元素
        console.log(e.currentTarget);  //监听事件的元素
    }
    a.addEventListener('click', handler);
</script>
</html>

点击内部蓝色区域,e.target和e.currentTarget分别输出如下。

console.log(e.target)输出

html 复制代码
<div id="inner"></div>

console.log(e.currentTarget)输出

html 复制代码
<div id="outer">
        <div id="inner"></div>
</div>

很明显,这个例子中监听是加在#outer上,而点击的是#inner,由此也直观地看到了e.target和e.currentTarget两者的区别,验证了e.currentTarget是监听事件的元素,e.target是触发事件的元素。

只是,这个汇率计算器困扰我的地方是,根据我最初的理解,在这个例子中,event.target指的是<li>元素,event.currentTarget也是<li>元素,两者应该是一致的,所以我把event.currentTarget换成了event.target。结果出乎意料,点击下方某种货币的时候,有时候正常,能够互换,有时候不正常,报错。我不明白造成这种现象的原因是什么,理论上说,要么对,要么不对,结果一会儿对,一会儿不对,这是什么状况?被这个问题困扰挺长时间。

后来仔细研究了一下<li>的结构,总算真相大白。

html 复制代码
   <li v-for="item in toList"
          :data-currency="item.currency"
          @click="changeCur">
        <span>{{item.currency}}</span>
        <span>{{item.amount}}</span>
   </li> 

<li>标记中有两个<span>标记,类似测试案例中的两个inner,我改成event.target后,在点击的时候,比较具有随意性,有时点击在<li>的空白处,有时点击在文字上,导致event.target有时候是<li>,有时候是<span>,所以就有时候正常,有时候不正常,总算想明白原因了。

总之,一番折腾后,发现这段代码中,必须使用event.currentTarget。

下面是实现汇率计算器的完整代码。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>汇率计算器</title>
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<!-- 人民币:CNY  港元:HKD  美元:USD  欧元:EUR 日元:JPY-->
  <style>
    h2.title {
      text-align: center;
      font-size:18px;
      margin: 30px 0 10px 0;
    }
    p.intro {
      text-align: center;
      font-size:14px;
    }
    ul {
      margin:0 auto;
      width: 200px;
      list-style-type: none;
      border:2px solid #999;
      border-radius: 10px;
      padding: 0;
      font-size: 16px;
      font-weight: bold;
      font-family: 'Courier New', Courier, monospace;
    }
    li {
      padding:10px;
    }
    li:first-child {
      display: flex;
      border-bottom: 2px solid #999;
    }
    li:not(:first-child):hover {
      background-color: #ddd;
    }
 
    span:last-child {
      float:right;
    }
    input {
      text-align: right;
      border: none;
      font-size: 16px;
      width:100px;
      margin-left:auto;
      font-family: 'Courier New', Courier, monospace;
      outline:none;
      border-bottom: 1px solid #000;
    }
  </style>
</head>
<body>
  <div id="app">
    <h2 class="title">汇率计算器</h2> 
    <ul>
      <li>
        <span>{{from.currency}}</span>
        <input v-model="from.amount"></input>
      </li>
      <!--data-currency 自定义属性,绑定货币名称,便于后期交换使用  -->
      <li v-for="item in toList"
          :data-currency="item.currency"
          @click="changeCur">
        <span>{{item.currency}}</span>
        <span>{{item.amount}}</span>
      </li> 
    </ul> 
    <p class="intro">鼠标点击可以切换货币种类</p>
  </div>

  <script>
    //汇率表
    let rate={
      '人民币':{'人民币':1     , '日元':16.876, '港元':1.1870, '美元':0.1526, '欧元':0.1294 },
      '日元':{'人民币':0.0595, '日元':1     , '港元':0.0702, '美元':0.0090, '欧元':0.0077 },
      '港元':{'人民币':0.8463, '日元':14.226, '港元':1     , '美元':0.1286, '欧元':0.10952},
      '美元':{'人民币':6.5813, '日元':110.62, '港元':7.7759, '美元':1     , '欧元':0.85164},
      '欧元':{'人民币':7.7278, '日元':129.89, '港元':9.1304, '美元':1.1742, '欧元':1      },
    }

    const app = Vue.createApp ({
      data() {
        return {
          from: {currency:'人民币', amount:100},
          toList:[
            {currency:'日元', amount:0}, 
            {currency:'港元', amount:0}, 
            {currency:'美元', amount:0}, 
            {currency:'欧元', amount:0}
          ]
        }
      },
      methods: {
        changeCur(event){
          const c = event.currentTarget.dataset.currency;  //获取点击的项的货币名称
          console.log(event.currentTarget);
          console.log(event.target);
          const f = this.from.currency;     //获取from项
          this.from.currency = c;          //点击项赋值给from
          this.toList.find(arritem => arritem.currency === c).currency = f;  //from项赋值给点击项
        },
        exchange(from, amount, to){
          return (amount * rate[from][to]).toFixed(2)
        },
      },
      //计算兑换后的金额,例如 美元amount=exchange(人民币,1000,美元)
      watch:{  //监听from
        from: {
          handler(value){
            this.toList.forEach(item => {
              item.amount = this.exchange(this.from.currency, 
                this.from.amount, item.currency)});  
          },
          deep:true,      //监听from对象里的currency、amount,deep需设置为true
          immediate:true  //页面一打开的时候,就执行一次
        }
      }
    }).mount('#app')
  </script>
</body>
</html>
相关推荐
Boilermaker199218 分钟前
【Java EE】SpringIoC
前端·数据库·spring
中微子29 分钟前
JavaScript 防抖与节流:从原理到实践的完整指南
前端·javascript
天天向上102444 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y1 小时前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁1 小时前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry1 小时前
Fetch 笔记
前端·javascript
拾光拾趣录1 小时前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟1 小时前
vue3,你看setup设计详解,也是个人才
前端
Lefan1 小时前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构