如何在Vue 3中实现子元素基于动态Props切换Active类
在Vue 3中实现“仅最近点击的Div保留Active类”的逻辑
我想把一段原生JavaScript逻辑迁移到Vue 3中:点击Div时,仅让最近被点击的Div保留active类,其余Div自动移除该类。对应的原生JS核心逻辑如下:
const listOfElements = document.getElementsByClassName('className'); for (i = 0; i < listOfElements.length; i++) { // 根据条件添加/移除类... }
我尝试通过父子组件的方式实现:点击Div触发子组件的emit事件,更新父组件状态后通过Props传递给子组件。以下是我编写的父子组件代码:
父组件代码
<template> <h2>参议院选区地图</h2> <p>选择一个参议院选区查看更多信息</p> <div> <div class="mapContainer"> <SenateDistrict :districtNumber="1" :width="20.3" :marginTop="0" :marginLeft="0" :currentActive="activeSenateDistrict" @updateActive="updateActive" > <svg xmlns="http://www.w3.org/2000/svg" class="district" viewBox="0 0 242.254 177.043"> <path id="Path_22" data-name="Path 22" d="M1.714,160.891H5.7v-6.174h92.39l11.585-11.575,13.13,11.19h9.943l8.158,8.151v9.4l4.658,4.655h9.581l12.128-12.119v-4.582l2.341-2.339-26.44-43.492,25-32.315H188.3l4.685-2.908L192.9,70.1h5.9l13.033,5.281,6.118-6.113h16.907l8.11-15.879V45.138H190.039l-5.466-5.462,3.656-12.914L174.618.5H1.714Z" transform="translate(-1.214 0)" stroke-miterlimit="10" stroke-width="1"/> </svg> </SenateDistrict> <SenateDistrict :districtNumber="2" :width="8.6" :marginTop="0" :marginLeft="14.8" :currentActive="activeSenateDistrict" @updateActive="updateActive" > <svg xmlns="http://www.w3.org/2000/svg" class="district" viewBox="0 0 104.24 103.79"> <path id="Path_23" data-name="Path 23" d="M184.822.5H287.734l-.12,91.373L271.684,102.8l.435-7.09-3.186-3.184,4.091-4.087-15.133-15.12L253.8,77.4l-7.929-7.921,7.349-14,.048-13.287H198.748l-4.175-3.521,3.62-12.733Z" transform="translate(-183.995 0)" stroke-miterlimit="10" stroke-width="1"/> </svg> </SenateDistrict> </div> </div> </template> <script> import SenateDistrict from '../../../components/SenateDistrict.vue' export default { name: 'SenateDistrictMap', components: { SenateDistrict }, data() { return { activeSenateDistrict: -1 } }, methods: { updateActive( senateDistrictNumber ) { this.activeSenateDistrict = senateDistrictNumber; } } } </script> <style scoped> .mapContainer { position: relative; font-size: 1vw; transition: 0.2s; } .district { width: 100%; height: 100%; } .district path { pointer-events: auto; cursor: pointer; transition-duration: .1s; transition-timing-function: ease-out; } </style>
子组件代码
<template> <div class="senateDistrict" :style="{ 'width': senateDistrictWidth, 'margin-top': senateDistrictMarginTop, 'margin-left': senateDistrictMarginLeft }" :class="{ active: isActive }" @click="updateActive" > <slot></slot> </div> </template> <script> export default { name: 'SenateDistrict', props: { districtNumber: { type: Number, required: true }, width: { type: Number, required: true }, marginTop: { type: Number, required: true }, marginLeft: { type: Number, required: true }, currentActive: { type: Number, required: true } }, computed: { senateDistrictWidth() { return `${this.width}em`; }, senateDistrictMarginTop() { return `${this.marginTop}em`; }, senateDistrictMarginLeft() { return `${this.marginLeft}em`; }, isActive() { return this.currentActive === this.districtNumber; } }, methods: { updateActive() { this.$emit('updateActive', this.districtNumber); } } } </script> <style scoped> .senateDistrict { position: absolute; top: 0em; left: 0em; pointer-events: none; fill: transparent; stroke: black; z-index: 4; } .senateDistrict:hover { fill: black; stroke: none; } .senateDistrict.active { fill: black; stroke: none; } </style>
关键修复说明
你的核心思路是正确的,但存在几个细节问题导致逻辑失效:
- 父组件绑定
currentActive时无需加this.,Vue模板中直接使用变量名即可,修正为:currentActive="activeSenateDistrict" - 子组件中
isActive不能放在data中(data仅初始化一次,父组件状态更新时无法同步),需改为计算属性,实时响应currentActive的变化 - 移除子组件中无效的
updateIsActive计算属性,直接通过计算属性返回状态判断结果 - 将
:class="[ isActive ? 'active': '']"简化为:class="{ active: isActive }",语法更清晰直观
修改完成后,点击不同选区时,父组件的activeSenateDistrict会更新,子组件通过计算属性实时判断是否添加active类,即可实现“仅最近点击的Div保留Active类”的需求。
内容的提问来源于stack exchange,提问作者Jtrom2021
相关产品推荐
相关产品推荐

