如何用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();
关键修改说明
- 初始化执行:页面加载时立即调用
handleMediaQuery,确保初始状态正确 - 事件监听管理:将所有事件处理函数提取为全局变量,停用滑块时通过
removeEventListeners移除所有监听 - 参数修复:给
deactivateJs设置默认参数slide=0,避免布局重置出错 - DOM冗余处理:激活滑块前先清空
dotContainer,防止重复创建导航dots - 状态保持:停用滑块时传入当前
curSlide,确保布局重置时位置正确
内容的提问来源于stack exchange,提问作者John Rowarth
相关产品推荐
相关产品推荐

