首页 > 解决方案 > 当我在移动设备上触摸我的 JavaScript 滑块时,我无法向下滚动页面

问题描述

我使用纯 JavaScript构建了一个滑块。在移动屏幕上,当我触摸滑块时,我只能更改幻灯片但不能向下滚动页面
当滑块元素被触摸时,触发“touchstart”事件和相应的事件处理函数,event.preventDefault()停止滚动页面,然后当“touchmove”事件触发时,代码使用第一个和新的水平坐标和CSS之间的差异向左移动滑块。

我在下面做了一个最小的代码。也可以点击查看在线编辑器上的代码

const slides = document.querySelector(".slides");
let posX1, posX2, dX;

slides.addEventListener("touchstart", dragStart);
slides.addEventListener("touchmove", dragAction);

function dragStart(e) {
  e.preventDefault();
  posX1 = e.touches[0].clientX;
}

function dragAction(e) {
  posX2 = e.touches[0].clientX;
  dX = posX2 - posX1;
  posX1 = e.touches[0].clientX;
  slides.style.left = (slides.offsetLeft + dX) + "px";
}
body {
  padding-bottom: 1000px;
}

.slider {
  width: 200px;
  height: 100px;
  margin: 0 auto;
  border: 1px solid black;
  position: relative;
  overflow: hidden;
}

.slides {
  width: 600px;
  height: 100px;
  display: flex;
  position: absolute;
}

.slide {
  width: 200px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.slide:nth-child(1) {
  background-color: rgb(200, 200, 200);
}

.slide:nth-child(2) {
  background-color: rgb(150, 150, 150);
}

.slide:nth-child(3) {
  background-color: rgb(100, 100, 100);
}
<!DOCTYPE html>
<html>

<body>
  <div class="slider">
    <div class="slides">
      <div class="slide">Slide 1</div>
      <div class="slide">Slide 2</div>
      <div class="slide">Slide 3</div>
    </div>
  </div>
</body>

</html>

谢谢你的时间 :)

标签: javascriptcarouseltouch-eventslidepreventdefault

解决方案


我想除了我之外没有人检查过我的问题,但是,假设有一个假设的人和我有同样的问题,我可以解决这个问题:
我想出了如何使用作者在“touchstart”中使用的这篇文章制作一个 JavaScript 滑块 EventListener 和我坚持使用它,但解决方案是简单地调用“touchmove”事件本身的方法而不是“ touchstart”,当然如果你需要(如果用户试图更改幻灯片)。如果用户试图滚动页面,则删除“touchend”EventListener。preventDefault()preventDefault()

const slides = document.querySelector(".slides");
let posX1,
    posX2,
    posY1,
    posY2,
    dX,
    dY,
    dirDetected = false;
    
  //feature detection
  //-------------Note 1-----------//
  let passiveIfSupported = false;
  try {
    window.addEventListener("test", null, Object.defineProperty({}, "passive", {
      get: function() {passiveIfSupported = {passive: false};}
    }));
  } catch(err) {}

slides.addEventListener("touchstart", dragStart, passiveIfSupported);
slides.addEventListener("touchmove", dragAction, passiveIfSupported);
slides.addEventListener("touchend", dragEnd, false);

function dragStart(e) {
  posX1 = e.touches[0].clientX;
  posY1 = e.touches[0].clientY;
}

function dragAction(e) {
  //-------------Note 2-----------//
  e.preventDefault();
  
  posX2 = e.touches[0].clientX;
  posY2 = e.touches[0].clientY;
  dX = posX2 - posX1;
  posX1 = e.touches[0].clientX;
  dY = posY2 - posY1;
  
  if (!dirDetected) {
    if (Math.abs(dY) > Math.abs(dX)) {
      slides.removeEventListener("touchmove", dragAction, passiveIfSupported);
      return;
    }
    dirDetected = true;
  }
    
  slides.style.left = (slides.offsetLeft + dX) + "px";
}

  function dragEnd() {
    if (!dirDetected) {
      slides.addEventListener("touchmove", dragAction, passiveIfSupported);
    }
    dirDetected = false;
  }
body {
  padding-bottom: 1000px;
}

.slider {
  width: 200px;
  height: 100px;
  margin: 0 auto;
  border: 1px solid black;
  position: relative;
  overflow: hidden;
}

.slides {
  width: 600px;
  height: 100px;
  display: flex;
  position: absolute;
}

.slide {
  width: 200px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.slide:nth-child(1) {
  background-color: rgb(200, 200, 200);
}

.slide:nth-child(2) {
  background-color: rgb(150, 150, 150);
}

.slide:nth-child(3) {
  background-color: rgb(100, 100, 100);
}
<!DOCTYPE html>
<html>

<body>
  <div class="slider">
    <div class="slides">
      <div class="slide">Slide 1</div>
      <div class="slide">Slide 2</div>
      <div class="slide">Slide 3</div>
    </div>
  </div>
</body>

</html>

注1.(addEventListener的第三个参数):注意addEventListener()方法的第三个参数。我从这个问题的搜索中意识到了一些事情。我将preventDefault()“touchmove” EventListener 中使用,但在某些版本的 Chrome 和 Firefox 中,如果您不指定选项对象的被动属性(的第三个参数),则默认值为“touchstart”“touchmove”addEventListener()事件设置为 true 并且这不会让 EventListener 调用preventDefault(),因此您必须将第三个参数设置为{passive: false}但如果加载脚本的浏览器是旧的,则需要第三个布尔参数addEventListener()那么你需要提供一个布尔值而不是对象。因此,您可以使用特征检测来提供适当的参数。

注意 2. Firefox 和 chrom android 处理“touchmove” preventDefault() 的方式不同:这是我发现的另一点(来自这个问题),如果你在 Chrome Android 上运行上述代码,它运行良好,你可以滚动页面或更改幻灯片但在Firefox Android 你不能滚动。实际上,我认为这是 Chrome 的错,因为根据“touchmove”事件的规范,如果第一个“touchmove”事件被阻止,那么随后绑定到同一触摸点的“touchmove”事件将被阻止,并且在上面的代码中的第一行我使用的“touchmove”事件处理函数preventDefault()所以我需要在 if 块之后调用它,以确保在需要时阻止默认行为。

const slides = document.querySelector(".slides");
let posX1,
    posX2,
    posY1,
    posY2,
    dX,
    dY,
    dirDetected = false;
    
  //feature detection
  let passiveIfSupported = false;
  try {
    window.addEventListener("test", null, Object.defineProperty({}, "passive", {
      get: function() {passiveIfSupported = {passive: false};}
    }));
  } catch(err) {}

slides.addEventListener("touchstart", dragStart, passiveIfSupported);
slides.addEventListener("touchmove", dragAction, passiveIfSupported);
slides.addEventListener("touchend", dragEnd, false);

function dragStart(e) {
  posX1 = e.touches[0].clientX;
  posY1 = e.touches[0].clientY;
}

function dragAction(e) {
  posX2 = e.touches[0].clientX;
  posY2 = e.touches[0].clientY;
  dX = posX2 - posX1;
  posX1 = e.touches[0].clientX;
  dY = posY2 - posY1;
  
  if (!dirDetected) {
    if (Math.abs(dY) > Math.abs(dX)) {
      slides.removeEventListener("touchmove", dragAction, passiveIfSupported);
      return;
    }
    e.preventDefault();
  }
  
  dirDetected = true;
  slides.style.left = (slides.offsetLeft + dX) + "px";
}

  function dragEnd() {
    if (!dirDetected) {
      slides.addEventListener("touchmove", dragAction, passiveIfSupported);
    }
    dirDetected = false;
  }
body {
  padding-bottom: 1000px;
}

.slider {
  width: 200px;
  height: 100px;
  margin: 0 auto;
  border: 1px solid black;
  position: relative;
  overflow: hidden;
}

.slides {
  width: 600px;
  height: 100px;
  display: flex;
  position: absolute;
}

.slide {
  width: 200px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.slide:nth-child(1) {
  background-color: rgb(200, 200, 200);
}

.slide:nth-child(2) {
  background-color: rgb(150, 150, 150);
}

.slide:nth-child(3) {
  background-color: rgb(100, 100, 100);
}
<!DOCTYPE html>
<html>

<body>
  <div class="slider">
    <div class="slides">
      <div class="slide">Slide 1</div>
      <div class="slide">Slide 2</div>
      <div class="slide">Slide 3</div>
    </div>
  </div>
</body>

</html>


推荐阅读