动态类名在 <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>
相关推荐
java1234_小锋5 小时前
【技术专题】HTML5 网页技术 - HTML5 Canvas 基础以及动画入门
前端·html·html5
知了学历孙老师8 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
ShineWinsu10 小时前
对于JavaScript:操作元素的解析
开发语言·前端·javascript·面试·web·dom·元素
福兮说11 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
程序员老赵12 小时前
Docker 部署青龙面板:轻松搭建定时任务管理平台
前端·docker·开源
Swot12 小时前
Asciidoctor 公式数字下标错位问题:原因分析与解决方案
前端
Swot12 小时前
leo-editor 中将 Markdown 内容转成 asciidoc 格式
前端
狒狒AI13 小时前
Remotion / GSAP+Three / HyperFrames / Skia 四栈实测:代码渲染知识视频的选栈方法(附开源技能)
前端·人工智能·aigc
修炼的dance13 小时前
浏览器里导出H.265,8.5秒和55.5秒差在硬件
前端
补码缩写13 小时前
WebCodecs选编码器,探测之后还要验解码和播放
前端