动态类名在 <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>
相关推荐
weixin_395448912 小时前
tidl_import_mul_rmfsd_psd_u8_3x480x544_bise_raw_dynamic.txt
java·服务器·前端
多多*2 小时前
图解Redis的分布式锁的历程 从单机到集群
java·开发语言·javascript·vue.js·spring·tomcat·maven
Jinuss3 小时前
源码分析之React中updateContainerImpl方法更新容器
前端·react.js·前端框架
Mr Xu_3 小时前
Vue + Element Plus 实现前端导出 Excel 功能详解
前端·javascript·vue.js
前端大波3 小时前
vue3的自动化路由(unplugin-vue-router)
javascript·vue.js·自动化
仰泳之鹅4 小时前
【杂谈】使用Edge浏览器下载文件显示“Microsoft Defender SmartScreen 已阻止此不安全文件”的解决方法
前端·edge
万邦科技Lafite4 小时前
小红书评论数据一键获取,item_reviewAPI接口讲解
大数据·前端·数据库·chrome·电商开放平台
多仔ヾ4 小时前
Vue.js 前端开发实战之 06-Vue 路由
vue.js
meng半颗糖5 小时前
vue3+tpescript 点击按钮跳转新页面直接通过链接预览word
前端·vue.js·word