You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 04:07:32