动态类名在 <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>
相关推荐
空&白1 小时前
vue暗黑模式
javascript·vue.js
梦帮科技1 小时前
Node.js配置生成器CLI工具开发实战
前端·人工智能·windows·前端框架·node.js·json
VT.馒头2 小时前
【力扣】2695. 包装数组
前端·javascript·算法·leetcode·职场和发展·typescript
css趣多多2 小时前
一个UI内置组件el-scrollbar
前端·javascript·vue.js
-凌凌漆-2 小时前
【vue】pinia中的值使用 v-model绑定出现[object Object]
javascript·vue.js·ecmascript
C澒2 小时前
前端整洁架构(Clean Architecture)实战解析:从理论到 Todo 项目落地
前端·架构·系统架构·前端框架
C澒2 小时前
Remesh 框架详解:基于 CQRS 的前端领域驱动设计方案
前端·架构·前端框架·状态模式
Charlie_lll2 小时前
学习Three.js–雪花
前端·three.js
onebyte8bits3 小时前
前端国际化(i18n)体系设计与工程化落地
前端·国际化·i18n·工程化
C澒3 小时前
前端分层架构实战:DDD 与 Clean Architecture 在大型业务系统中的落地路径与项目实践
前端·架构·系统架构·前端框架