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

修复无限循环Banner轮播切换时出现重复原Banner的问题

修复Banner无限切换时的重复显示问题

问题描述

我编写了一段实现Banner切换及无限循环功能的JavaScript代码,但在Banner切换过渡过程中,会先出现一个与原Banner相同的额外Banner,再切换至目标Banner。原代码如下:

原HTML代码

<div class="slider">
  <div class="slider-banners">
    <div class="slider-banner red">red</div>
    <div class="slider-banner blue">blue</div>
    <div class="slider-banner green">green</div>
  </div>
  <button class="slider-toggle slider-toggle-left">&#8249;</button>
  <button class="slider-toggle slider-toggle-right">&#8250;</button>
</div>

原CSS代码

.slider {
  position: relative;
  width: 100%;
  height: 300px;
  overflow: hidden;
}

.slider-banners {
  position: absolute;
  top: 0;
  left: 0;
  width: 300%;
  height: 100%;
  display: flex;
  transition: transform 0.5s ease-out;
  color: #fff;
}

.slider-banner {
  flex-shrink: 0;
  width: 100%;
  height: 100%;
}

.red {
  background-color: red;
}

.blue {
  background-color: blue;
}

.green {
  background-color: green;
}

.slider-toggle {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  border: none;
  background-color: rgba(0, 0, 0, 0.5);
  color: white;
  font-size: 24px;
  cursor: pointer;
  transition: opacity 0.2s;
}

.slider-toggle:hover {
  opacity: 0.8;
}

.slider-toggle-left {
  left: 0;
}

.slider-toggle-right {
  right: 0;
}

原JS代码

const slider = document.querySelector('.slider');
const banners = document.querySelectorAll('.slider-banner');
const toggleLeft = document.querySelector('.slider-toggle-left');
const toggleRight = document.querySelector('.slider-toggle-right');
let currentBanner = 0;
const firstBannerClone = banners[0].cloneNode(true);
slider.querySelector('.slider-banners').appendChild(firstBannerClone);

function switchBanner(direction) {
  if (direction === 'left') {
    currentBanner--;
    if (currentBanner < 0) {
      currentBanner = banners.length;
      slider.querySelector('.slider-banners').style.transition = 'none';
      slider.querySelector('.slider-banners').style.transform = `translateX(-${(banners.length) * 100}%)`;
      setTimeout(() => {
        slider.querySelector('.slider-banners').style.transition = 'transform 0.5s ease-out';
        currentBanner = banners.length - 1;
        slider.querySelector('.slider-banners').style.transform = `translateX(-${currentBanner * 100}%)`;
      }, 50);
    }
  } else if (direction === 'right') {
    currentBanner++;
    if (currentBanner > banners.length) {
      currentBanner = 0;
      slider.querySelector('.slider-banners').style.transition = 'none';
      slider.querySelector('.slider-banners').style.transform = `translateX(-${currentBanner * 100}%)`;
      setTimeout(() => {
        slider.querySelector('.slider-banners').style.transition = 'transform 0.5s ease-out';
        currentBanner = 1;
        slider.querySelector('.slider-banners').style.transform = `translateX(-${currentBanner * 100}%)`;
      }, 50);
    }
  } else {
    return;
  }
  slider.querySelector('.slider-banners').style.transform = `translateX(-${currentBanner * 100}%)`;
}
toggleLeft.addEventListener('click', () => {
  switchBanner('left');
});
toggleRight.addEventListener('click', () => {
  switchBanner('right');
});

问题原因分析

  1. 容器宽度固定导致显示异常:CSS中.slider-banners的width写死为300%,但克隆第一个Banner后容器内共有4个Banner,宽度应为400%,固定值无法适配动态变化的Banner数量。
  2. 多余的transform覆盖逻辑:switchBanner函数末尾的slider.querySelector('.slider-banners').style.transform语句,会覆盖分支内的边界处理逻辑,导致视觉上出现重复Banner的闪回。
  3. 边界判断条件错误:右滑时的判断条件currentBanner > banners.length不准确,克隆后容器的最大有效索引是banners.length(原Banner数量为3,克隆后第4个元素索引为3),应改为currentBanner >= banners.length。

修复后的完整代码

HTML代码(无需修改)

<div class="slider">
  <div class="slider-banners">
    <div class="slider-banner red">red</div>
    <div class="slider-banner blue">blue</div>
    <div class="slider-banner green">green</div>
  </div>
  <button class="slider-toggle slider-toggle-left">&#8249;</button>
  <button class="slider-toggle slider-toggle-right">&#8250;</button>
</div>

CSS代码

.slider {
  position: relative;
  width: 100%;
  height: 300px;
  overflow: hidden;
}

.slider-banners {
  position: absolute;
  top: 0;
  left: 0;
  /* 动态计算容器宽度,适配Banner数量 */
  width: calc(var(--banner-count) * 100%);
  height: 100%;
  display: flex;
  transition: transform 0.5s ease-out;
  color: #fff;
}

.slider-banner {
  flex-shrink: 0;
  width: 100%;
  height: 100%;
}

.red {
  background-color: red;
}

.blue {
  background-color: blue;
}

.green {
  background-color: green;
}

.slider-toggle {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 30px;
  height: 30px;
  border: none;
  background-color: rgba(0, 0, 0, 0.5);
  color: white;
  font-size: 24px;
  cursor: pointer;
  transition: opacity 0.2s;
}

.slider-toggle:hover {
  opacity: 0.8;
}

.slider-toggle-left {
  left: 0;
}

.slider-toggle-right {
  right: 0;
}

JavaScript代码

const slider = document.querySelector('.slider');
const banners = document.querySelectorAll('.slider-banner');
const toggleLeft = document.querySelector('.slider-toggle-left');
const toggleRight = document.querySelector('.slider-toggle-right');
const sliderBanners = slider.querySelector('.slider-banners'); // 缓存DOM元素,减少重复查询
let currentBanner = 0;
const firstBannerClone = banners[0].cloneNode(true);
sliderBanners.appendChild(firstBannerClone);

// 设置CSS变量,动态计算容器宽度
sliderBanners.style.setProperty('--banner-count', banners.length + 1);

function switchBanner(direction) {
  if (direction === 'left') {
    currentBanner--;
    if (currentBanner < 0) {
      // 跳转到克隆的第一个Banner(无过渡)
      currentBanner = banners.length;
      sliderBanners.style.transition = 'none';
      sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
      setTimeout(() => {
        // 开启过渡,切换到最后一个原Banner
        sliderBanners.style.transition = 'transform 0.5s ease-out';
        currentBanner = banners.length - 1;
        sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
      }, 50);
    } else {
      // 正常左滑过渡
      sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
    }
  } else if (direction === 'right') {
    currentBanner++;
    if (currentBanner >= banners.length) {
      // 跳转到第一个原Banner(无过渡)
      currentBanner = 0;
      sliderBanners.style.transition = 'none';
      sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
      setTimeout(() => {
        // 开启过渡,切换到第二个Banner
        sliderBanners.style.transition = 'transform 0.5s ease-out';
        currentBanner = 1;
        sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
      }, 50);
    } else {
      // 正常右滑过渡
      sliderBanners.style.transform = `translateX(-${currentBanner * 100}%)`;
    }
  }
}

toggleLeft.addEventListener('click', () => switchBanner('left'));
toggleRight.addEventListener('click', () => switchBanner('right'));

修复要点说明

  • 动态容器宽度:通过CSS变量--banner-count动态计算.slider-banners的宽度,适配任意数量的Banner,避免固定宽度导致的布局错误。
  • 移除多余的transform语句:删除函数末尾的全局transform设置,确保分支内的边界处理逻辑不会被覆盖。
  • 修正边界判断条件:将右滑的判断条件改为currentBanner >= banners.length,准确触发边界跳转逻辑。
  • 缓存DOM元素:提前缓存sliderBanners元素,减少重复DOM查询,提升性能。

内容的提问来源于stack exchange,提问作者Nguyễn Nguyễn Hữu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 13:37:08