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>
相关推荐
有梦想的咕噜3 分钟前
Electron 是一个用于构建跨平台桌面应用程序的开源框架
前端·javascript·electron
yqcoder5 分钟前
electron 监听窗口高端变化
前端·javascript·vue.js
bjzhang7511 分钟前
Depcheck——专门用于检测 JavaScript 和 Node.js 项目中未使用依赖项的工具
javascript·node.js·depcheck
Python私教21 分钟前
Flutter主题最佳实践
前端·javascript·flutter
于慨26 分钟前
Flutter实战短视频课程
javascript
wangshuai092729 分钟前
vue使用prototype
vue.js
GDAL40 分钟前
HTML入门教程7:HTML样式
前端·html
生命几十年3万天1 小时前
解决edge浏览器无法同步问题
前端·edge
小明铭同学1 小时前
JavaScript常用库
javascript