带可点击卡片的Carousel滑动与点击冲突问题求助
问题
我制作了一个轮播组件,卡片div被包裹在<a>标签内供用户点击。目前仅能滑动轮播,但卡片无法点击;若移除.inner-slider的pointer-events: none;样式,卡片可正常点击,但轮播滑动变得卡顿跳帧。如何解决这个矛盾?
原代码片段
JavaScript
let sliderContainer = document.querySelector("\.slider-container"); let innerSlider = document.querySelector("\.inner-slider"); let banner = document.querySelector("\.banner"); let pressed = false; let startX; let x; let bannerOpacity = banner.style.opacity let oldx = 0 sliderContainer.addEventListener("mousedown", (e) => { pressed = true; startX = e.offsetX - innerSlider.offsetLeft; sliderContainer.style.cursor = "grabbing"; }); sliderContainer.addEventListener("mouseleave", () => { sliderContainer.style.cursor = "default"; }); sliderContainer.addEventListener("mouseup", () => { sliderContainer.style.cursor = "grab"; pressed = false; }); window.addEventListener("mouseup", () => { // pressed = false; }); sliderContainer.addEventListener("mousemove", (e) => { if (!pressed) return; console.log(`e.pageX:${e.pageX};oldx:${oldx};op:${banner.style.opacity}`); console.log(banner.style.opacity + 1); if (e.pageX > oldx && banner.style.opacity < 1) { banner.style.opacity = parseFloat(banner.style.opacity) + 0.01; console.log('right'); } else if (e.pageX < oldx && banner.style.opacity > 0) { console.log('left'); banner.style.opacity -= 0.01 } e.preventDefault(); x = e.offsetX; innerSlider.style.left = `${x - startX}px`; oldx = e.pageX; checkBoundary(); }); const checkBoundary = () => { let outer = sliderContainer.getBoundingClientRect(); let inner = innerSlider.getBoundingClientRect(); if (parseInt(innerSlider.style.left) > 150) { innerSlider.style.left -= 10; // bannerOpacity -= 0.01 } if (inner.right < outer.right) { innerSlider.style.left = `-${inner.width - outer.width}px`; } };
CSS
.card { height: 300px; width: 400px; border-radius: 5px; } .banner { z-index: 2; height: 300px; width: 200px; border-radius: 5px; background-color: red; } .card:nth-child(odd) { background-color: blue; } .card:nth-child(even) { background-color: rgb(0, 183, 255); } .slider-container { width: 80%; height: 350px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); overflow: hidden; } .inner-slider { width: 150%; display: flex; gap: 10px; pointer-events: none; position: absolute; top: 0; left: 250px; }
HTML
<div class="slider-container"> <div class="banner" style="opacity: 1;"></div> <div class="inner-slider"> <a href="google.com"> <div class="card"></div> </a> <a href="https://www.google.com/"> <div class="card"></div> </a> <a href="https://www.google.com/"> <div class="card"></div> </a> <a href="https://www.google.com/"> <div class="card"></div> </a> <a href="https://www.google.com/"> <div class="card"></div> </a> </div> </div>
解决方案
核心思路是只在滑动过程中禁用.inner-slider的指针事件,静止时恢复,这样既保证滑动流畅,又不影响卡片点击。
具体修改步骤
- 移除
.inner-slider默认的pointer-events: none;样式 - 在
mousedown事件触发(开始滑动)时,给.inner-slider添加pointer-events: none; - 在
mouseup事件触发(滑动结束)时,移除.inner-slider的pointer-events: none;
修改后的代码
CSS
.card { height: 300px; width: 400px; border-radius: 5px; } .banner { z-index: 2; height: 300px; width: 200px; border-radius: 5px; background-color: red; } .card:nth-child(odd) { background-color: blue; } .card:nth-child(even) { background-color: rgb(0, 183, 255); } .slider-container { width: 80%; height: 350px; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); overflow: hidden; } .inner-slider { width: 150%; display: flex; gap: 10px; /* 移除默认的pointer-events: none */ position: absolute; top: 0; left: 250px; }
JavaScript
let sliderContainer = document.querySelector(".slider-container"); let innerSlider = document.querySelector(".inner-slider"); let banner = document.querySelector(".banner"); let pressed = false; let startX; let x; let bannerOpacity = parseFloat(banner.style.opacity); let oldx = 0; sliderContainer.addEventListener("mousedown", (e) => { pressed = true; startX = e.offsetX - innerSlider.offsetLeft; sliderContainer.style.cursor = "grabbing"; // 开始滑动时禁用inner-slider的指针事件,避免点击事件干扰滑动 innerSlider.style.pointerEvents = "none"; }); sliderContainer.addEventListener("mouseleave", () => { sliderContainer.style.cursor = "default"; }); sliderContainer.addEventListener("mouseup", () => { sliderContainer.style.cursor = "grab"; pressed = false; // 滑动结束恢复指针事件,允许点击卡片 innerSlider.style.pointerEvents = "auto"; }); window.addEventListener("mouseup", () => { pressed = false; // 全局mouseup也恢复指针事件,防止鼠标移出容器后无法恢复 innerSlider.style.pointerEvents = "auto"; }); sliderContainer.addEventListener("mousemove", (e) => { if (!pressed) return; // 修复opacity计算的类型问题,避免字符串拼接错误 if (e.pageX > oldx && bannerOpacity < 1) { bannerOpacity += 0.01; banner.style.opacity = bannerOpacity; } else if (e.pageX < oldx && bannerOpacity > 0) { bannerOpacity -= 0.01; banner.style.opacity = bannerOpacity; } e.preventDefault(); x = e.offsetX; innerSlider.style.left = `${x - startX}px`; oldx = e.pageX; checkBoundary(); }); const checkBoundary = () => { let outer = sliderContainer.getBoundingClientRect(); let inner = innerSlider.getBoundingClientRect(); if (parseInt(innerSlider.style.left) > 150) { // 修复left赋值的语法问题,原代码会把字符串和数字拼接导致错误 innerSlider.style.left = `${parseInt(innerSlider.style.left) - 10}px`; } if (inner.right < outer.right) { innerSlider.style.left = `-${inner.width - outer.width}px`; } };
额外说明
- 滑动时禁用指针事件,避免
<a>标签的点击事件与滑动事件冲突,彻底解决卡顿问题 - 滑动结束后立即恢复,用户可以正常点击卡片跳转
- 修复了原代码中
banner.style.opacity的类型转换问题,以及innerSlider.style.left的赋值语法错误,避免出现异常
内容的提问来源于stack exchange,提问作者Mike
相关产品推荐
相关产品推荐

