动态类名在 <swiper-slide 的复制项中没有起作用的解决方法

动态类名在 <swiper-slide 的复制项中没有起作用的解决方法

错误代码

javascript 复制代码
<swiper :options="swiperOption" ref="mySwiper" class="bottom-ul">
      <swiper-slide v-for="(d, i) in bottomData" :key="i" class="menu-item">
        <!--  @click="btnClick(d.groupName)" -->
        <li
         
          class="menu-li"
          :class="{ active: activeGroup === d.groupName }"
        >
          <!-- <i style="">
          <img :src="d.src">
        </i> -->
          <span>{{ d.groupName }}</span>
        </li>
      </swiper-slide>

核心原因是复制的 slide 是 Swiper 纯 DOM 层面的复制,不会触发 Vue 的响应式渲染,所以复制项里的 Vue 指令(如 :class)完全失效。

问题本质拆解

v-for 只渲染了真实的 slide,这些 slide 内的 <li> 受 Vue 响应式控制,active 类名正常;

Swiper 开启 loop 后,会在 DOM 层面复制真实 slide 生成 duplicate 项,但复制的只是静态 DOM 结构,没有 Vue 的响应式绑定,所以 :class 指令不会执行,active 类名自然不会出现在复制项的 <li> 上;

当切换到复制 slide 时,视觉上看到的是复制项的 <li>,但它没有 active 类,导致选中样式丢失。

解决方案:手动同步复制项的 active 类

核心思路是:监听 Swiper 的切换事件和 Vue 的 activeGroup 变化,手动给真实 slide + 复制 slide 中匹配的 <li> 添 / 删 active 类,绕过 Vue 响应式的限制。

javascript 复制代码
<template>
  <div>
    <swiper 
      :options="swiperOption" 
      ref="mySwiper" 
      class="bottom-ul"
      @slideChange="handleSlideChange"
    >
      <swiper-slide 
        v-for="(d, i) in bottomData" 
        :key="i" 
        class="menu-item"
        :data-group-name="d.groupName"  <!-- 给真实slide标记分组名,方便DOM查找 -->
      >
        <li
          class="menu-li"
          :class="{ active: activeGroup === d.groupName }"
        >
          <span>{{ d.groupName }}</span>
        </li>
      </swiper-slide>
    </swiper>
  </div>
</template>

<script>
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
import SwiperCore from 'swiper';

export default {
  components: {
    Swiper,
    SwiperSlide
  },
  data() {
    return {
      bottomData: [  // 示例数据
        { groupName: '分组1' },
        { groupName: '分组2' },
        { groupName: '分组3' },
        { groupName: '分组4' }
      ],
      activeGroup: '分组2',  // 默认选中的分组
      swiperOption: {
        centeredSlides: true,  // 居中展示
        loop: true,            // 循环(导致复制slide)
        slidesPerView: 'auto', // 自适应宽度
        loopAdditionalSlides: 5, // 增加复制slide数量
        initialSlide: 1,       // 初始从真实slide开始
        centeredSlidesBounds: false
      }
    };
  },
  watch: {
    // 监听activeGroup变化,同步更新所有slide(真实+复制)的active类
    activeGroup(newVal) {
      this.syncActiveClass(newVal);
    }
  },
  mounted() {
    // 初始化时同步一次active类(确保复制项也有初始选中样式)
    this.$nextTick(() => {
      this.syncActiveClass(this.activeGroup);
    });
  },
  methods: {
    // 核心方法:同步真实slide和复制slide的active类
    syncActiveClass(activeName) {
      if (!this.$refs.mySwiper) return;
      
      // 1. 先移除所有slide内li的active类(避免多个active)
      document.querySelectorAll('.bottom-ul .menu-li').forEach(li => {
        li.classList.remove('active');
      });
      
      // 2. 找到所有包含目标groupName的slide(真实+复制),给其li加active类
      const targetSlides = document.querySelectorAll(`.bottom-ul .menu-item[data-group-name="${activeName}"]`);
      targetSlides.forEach(slide => {
        const li = slide.querySelector('.menu-li');
        if (li) li.classList.add('active');
      });
    },

    // Swiper切换事件:同步activeGroup和active类
    handleSlideChange() {
      const swiperInstance = this.$refs.mySwiper.swiper;
      // 获取当前激活slide的真实索引
      const realIndex = swiperInstance.realIndex;
      // 更新activeGroup为当前真实slide对应的分组名
      this.activeGroup = this.bottomData[realIndex].groupName;
      // 同步复制slide的active类
      this.syncActiveClass(this.activeGroup);
    },

    // 如果你有手动点击li的方法,也要同步调用syncActiveClass
    btnClick(groupName) {
      this.activeGroup = groupName;
      this.syncActiveClass(groupName);
      // 可选:点击后让对应的slide居中
      const swiperInstance = this.$refs.mySwiper.swiper;
      const targetIndex = this.bottomData.findIndex(d => d.groupName === groupName);
      swiperInstance.slideTo(targetIndex + 1, 300, true); // +1 适配loop的索引偏移
    }
  }
};
</script>

<style scoped>
/* 示例样式:方便看选中效果 */
.menu-li {
  list-style: none;
  padding: 8px 16px;
  cursor: pointer;
}
.menu-li.active {
  background-color: #409eff;
  color: white;
  border-radius: 4px;
}
.bottom-ul {
  width: 100%;
  overflow: hidden;
}
.menu-item {
  display: inline-block;
  margin: 0 8px;
}
</style>
相关推荐
Fairy要carry11 小时前
面试-Dispatch Tools
前端·chrome
IT_陈寒11 小时前
JavaScript开发者必看:3个让代码效率翻倍的隐藏技巧
前端·人工智能·后端
嘉琪00111 小时前
Day8 完整学习包(Vue 基础 & 响应式)——2026 0320
前端·vue.js·学习
FlyWIHTSKY11 小时前
Vue3 单文件中不同的组件
前端·javascript·vue.js
一字白首12 小时前
小程序组件化进阶:从复用到通信的完整指南DAY04
前端·小程序·apache
读忆12 小时前
你是否用过Tailwind CSS?你是在什么情况下使用的呢?
前端·css·经验分享·笔记·taiiwindcss
阿珊和她的猫12 小时前
探秘小程序:为何拿不到 DOM 相关 API
前端·小程序
FlyWIHTSKY12 小时前
Vue 3 onMounted 中控制同步与异步执行策略
前端·javascript·vue.js
PascalMing12 小时前
告别 Nginx!ASP.NET Core 实现多域名 Vue 静态服务与代理转发
vue.js·nginx·asp.net
蜗牛攻城狮12 小时前
【Vue3实战】El-Table实现“超过3行省略,悬停显示全文”的完美方案(附性能优化)
前端·vue.js·性能优化·element-plus