当我在移动设备上触摸 JavaScript 滑块时,无法向下滚动页面

When I touch my JavaScript slider on mobile, I can't scroll down the page

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

我在下面做了一个最小的代码。同时点击see code on online editor.

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>

感谢您的宝贵时间:)

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

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()方法的第三个参数。我在 . I'm going to use preventDefault() inside "touchmove" EventListener but in some versions of Chrome and Firefox for "touchstart" and "touchmove" events if you don't specify passive property of options object (the third parameter of addEventListener()) the default value would be set to true and this doesn't let the EventListener to call preventDefault() so you have to set the third parameter to {passive: false} but if the browser loading your script is an old one which requires a third boolean parameter for addEventListener() then you need to provide a boolean value not object. So you can use feature detection 的搜索中发现了一些提供适当参数的东西。

注意 2. Firefox 和 chrom android 处理“touchmove”preventDefault() 的方式不同: 这是我发现的另一点 (from this question), if you run the above code on Chrome Android it works well, you can scroll the page or change slides but on Firefox Android you can't scroll. Actually I think it's a Chrome's fault because according to Specs for "touchmove" event 如果第一个 "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>