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

如何用JavaScript实现响应式媒体查询?滑块JS动态启停问题

解决 testimonial slider 响应式动态启用/停用问题

我制作了一个testimonial slider,希望在屏幕宽度达到800px断点时,停用滑块的JavaScript并更改布局。目前的问题是,JS激活后无法动态停用,只能通过刷新页面实现停用,想要无需刷新的响应式效果。

问题根源

你的代码存在以下几个关键问题:

  • 页面初始化时未执行媒体查询判断,只有窗口尺寸变化时才触发逻辑
  • 停用滑块时没有移除之前绑定的事件监听器,按钮、键盘操作依然会触发滑块逻辑
  • 调用deactivateJs时未传参,导致slide参数为undefined,布局重置出错
  • 重复激活滑块时会多次生成导航dots,造成DOM冗余

修复后的完整代码

const mQ = window.matchMedia('(max-width: 800px)');

const testimonialSlider = function () {
  const slides = document.querySelectorAll('.slide');
  const btnLeft = document.querySelector('.slider__btn--left');
  const btnRight = document.querySelector('.slider__btn--right');
  const dotContainer = document.querySelector('.dots');
  const testimonialContainer = document.querySelector('.testimonial-outer-container');

  // 声明全局事件处理函数,方便后续移除监听
  let nextSlide, prevSlide, keydownHandler, dotClickHandler;
  let curSlide = 0;
  const maxSlide = slides.length;

  // 停用滑块逻辑
  const deactivateJs = function (slide = 0) {
    // 重置所有slide的位置
    slides.forEach((s, i) => (s.style.transform = `translateX(${0 * (i - slide)}%)`));
    // 移除导航dots容器
    if (dotContainer.parentElement === testimonialContainer) {
      testimonialContainer.removeChild(dotContainer);
    }
    // 移除所有事件监听
    removeEventListeners();
    console.log('滑块已停用');
  };

  // 移除事件监听器
  const removeEventListeners = function () {
    if (nextSlide) btnRight.removeEventListener('click', nextSlide);
    if (prevSlide) btnLeft.removeEventListener('click', prevSlide);
    if (keydownHandler) document.removeEventListener('keydown', keydownHandler);
    if (dotClickHandler) dotContainer.removeEventListener('click', dotClickHandler);
  };

  // 激活滑块逻辑
  const activateJs = function () {
    console.log('滑块已激活');
    curSlide = 0;

    // 重置dot容器
    dotContainer.innerHTML = '';
    testimonialContainer.appendChild(dotContainer);

    // 创建导航dots
    const createDots = function () {
      slides.forEach(function (_, i) {
        dotContainer.insertAdjacentHTML(
          'beforeend',
          `<button class="dots__dot" data-slide="${i}"></button>`
        );
      });
    };

    // 切换到指定slide
    const goToSlide = function (slide) {
      slides.forEach(
        (s, i) => (s.style.transform = `translateX(${100 * (i - slide)}%)`)
      );
    };

    // 激活指定dot
    const activateDot = function (slide) {
      document.querySelectorAll('.dots__dot').forEach(dot => dot.classList.remove('dots__dot--active'));
      document.querySelector(`.dots__dot[data-slide="${slide}"]`).classList.add('dots__dot--active');
    };

    // 下一个slide
    nextSlide = function () {
      curSlide = curSlide === maxSlide - 1 ? 0 : curSlide + 1;
      goToSlide(curSlide);
      activateDot(curSlide);
    };

    // 上一个slide
    prevSlide = function () {
      curSlide = curSlide === 0 ? maxSlide - 1 : curSlide - 1;
      goToSlide(curSlide);
      activateDot(curSlide);
    };

    // 键盘事件处理
    keydownHandler = function (e) {
      if (e.key === 'ArrowLeft') prevSlide();
      if (e.key === 'ArrowRight') nextSlide();
    };

    // Dot点击事件处理
    dotClickHandler = function (e) {
      if (e.target.classList.contains('dots__dot')) {
        const { slide } = e.target.dataset;
        curSlide = Number(slide);
        goToSlide(slide);
        activateDot(slide);
      }
    };

    // 初始化滑块状态
    const slideReady = function () {
      createDots();
      activateDot(0);
      goToSlide(0);
    };

    slideReady();

    // 绑定事件监听器
    btnRight.addEventListener('click', nextSlide);
    btnLeft.addEventListener('click', prevSlide);
    document.addEventListener('keydown', keydownHandler);
    dotContainer.addEventListener('click', dotClickHandler);
  };

  // 媒体查询状态处理
  const handleMediaQuery = function () {
    if (mQ.matches) {
      deactivateJs(curSlide);
    } else {
      activateJs();
    }
  };

  // 初始化执行一次,确保页面加载时状态正确
  handleMediaQuery();
  // 监听窗口尺寸变化
  mQ.addEventListener('change', handleMediaQuery);
};

testimonialSlider();

关键修改说明

  1. 初始化执行:页面加载时立即调用handleMediaQuery,确保初始状态正确
  2. 事件监听管理:将所有事件处理函数提取为全局变量,停用滑块时通过removeEventListeners移除所有监听
  3. 参数修复:给deactivateJs设置默认参数slide=0,避免布局重置出错
  4. DOM冗余处理:激活滑块前先清空dotContainer,防止重复创建导航dots
  5. 状态保持:停用滑块时传入当前curSlide,确保布局重置时位置正确

内容的提问来源于stack exchange,提问作者John Rowarth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 17:50:37