当您模拟智能手机时,输入范围在可滚动 div 中不起作用

Input range won't work in an scrollable div when you simulate a smartphone

我在 div 中有一个 input range 包含在一个比内部 div 小的外部 div 中。最后的结果是我的内层div水平滚动(因为外层潜水有overflow: scroll),而input range是它的子

为了自定义范围,我用 appearance: none 删除了 CSS 中的外观。现在,这就是发生的事情。当我在 Chrome 开发者工具中检查它时(实际上我使用的是 Brave,但我猜它在 Chrome 中与我在 Chromium 中测试的相同,并且是相同的)并且智能手机选项处于活动状态,99% 的时间,如果我尝试移动范围手柄,它会移动整个 div。现在,如果我禁用智能手机选项,它就可以正常工作。此外,如果我保留智能手机选项并从 CSS 中删除 appearance: none,它也可以正常工作,但我对范围的自定义会消失。有人知道这是怎么回事吗?

PS.: 在 Firefox 中,只要我保持智能手机选项打开,input range 就不起作用(无论我是否有外观 属性) .

这是我的意思的动画 gif: 首先,我上面描述的 input range 没有出现。它工作正常,我可以移动可滚动的 div 并独立移动 input range 手柄。然后我将 appearance: none 放到 input range (注意输入范围的格式发生变化),现在我不能再独立于可滚动 div 移动 input range 句柄.最后,从 input range 中删除外观,一切恢复正常(但我的自定义设置消失了)

这是代码,但您只能使用开发者工具在智能手机选项处于活动状态的情况下进行模拟,您可以在其中模拟触摸。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
  <meta charset="UTF-8">
  <style>
    #outerframe {
      height: 310px;
      width: 300px;
      border: 2px solid red;
      padding: 8px;
      margin: auto;
      overflow: scroll;
    }

    #scrollarea {
      height: 300px;
      width: 400px;
      border: 1px dashed green;
      padding: 16px;
    }

    input[type=range] {
      appearance: none;
      background: linear-gradient(90deg, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
      outline: none;
      opacity: 0.7;
      border-radius: 4px;
      height: 14px;
      width: 200px;
      box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
      vertical-align: middle;
      margin: 16px;
    }

    input[type=text] {
      width: 50px;
    }
  </style>
  <title>Testing range in scroll</title>
</head>
<body>
  <div id="outerframe">
    <div id="scrollarea">
      <input type="range" id="rangescroll">
      <input type="text" value="50">
    </div>
  </div>

  <script>
    scrollarea.firstElementChild.addEventListener('input', evt => {
      evt.target.nextElementSibling.value = evt.target.value;
    })
  </script>
</body>
</html>

编辑: 修复:

Oleg Barabanov,建议在移动输入句柄时制作 overflow: hidden(在 touchstart and removing the class on touchend). While that works I thought about a more elegant solution. I remembered about event.preventDefault() 上为此添加 class 以及它如何允许我们取消浏览器对后续事件的处理. 这个想法是,如果我可以允许一个事件,input 事件并阻止其余事件(浏览器自动完成的滚动事件),那么它就可以工作。

这是我第一次尝试输入范围的附加代码:

    document.querySelector('.scrollarea').firstElementChild.addEventListener("touchstart", (evt) =>

document.querySelector('.outerframe').classList.add("overflow-hidden");
      evt.preventDefault();
    });

它在这里不起作用(这让我感到惊讶,因为我过去曾成功地使用它来停止默认事件)。但是后来我想起来我在研究 JS 事件的时候还有一种方法可以防止默认事件,那就是 return false for the event:

    document.querySelector('.scrollarea').firstElementChild.addEventListener("touchstart", (evt) =>

document.querySelector('.outerframe').classList.add("overflow-hidden");
      return false;
    });

公平地说,那篇文章提到 return false 仅适用于将事件配置为 ontouchstart 等属性的情况。但是我使用了 addEventListener() 方法,它也有效。

希望以后能帮助到更多的人

我不清楚具体原因,但是当您在移动测试视图中时,在开发工具中禁用 #scrollarea 中的 heightwidth 可以解决问题。移动视图中的 #scrollarea 是通过移动参数内的所有内容来处理的。 另外两个避免必须删除参数的解决方案是在 #rangescroll.

上设置 position=fixedposition=absolute

如果这是浏览器支持的问题,您可以尝试在线工具来帮助生成 CSS 支持跨浏览器的代码。 所以我建议你可以去 here

这是生成的代码:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
  <meta charset="UTF-8">
  <style>
    #outerframe {
      height: 310px;
      width: 300px;
      border: 2px solid red;
      padding: 8px;
      margin: auto;
      overflow: scroll;
    }
    
    #scrollarea {
      height: 300px;
      width: 400px;
      border: 1px dashed green;
      padding: 16px;
    }
    
    input[type=range] {
      -webkit-appearance: none;
      -moz-appearance: none;
      appearance: none;
      background: -webkit-gradient(linear, left top, right top, from(#aaa), color-stop(30%, #ccf1ff), color-stop(70%, #ccf1ff), to(#aaa));
      background: -o-linear-gradient(left, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
      background: linear-gradient(90deg, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
      outline: none;
      opacity: 0.7;
      border-radius: 4px;
      height: 14px;
      width: 200px;
      -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
      box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
      vertical-align: middle;
      margin: 16px;
    }
    
    input[type=text] {
      width: 50px;
    }
  </style>
  <title>Testing range in scroll</title>
</head>

<body>
  <div id="outerframe">
    <div id="scrollarea">
      <input type="range" id="rangescroll">
      <input type="text" value="50">
    </div>
  </div>

  <script>
    scrollarea.firstElementChild.addEventListener('input', evt => {
      evt.target.nextElementSibling.value = evt.target.value;
    })
  </script>
</body>

</html>

在移动设备中,您可以通过拖动来上下滚动,也可以左右滚动。如果您只是在 #outerframe 上禁用 overflow-x,您可以解决您的问题:

    #outerframe {
      height: 310px;
      width: 300px;
      border: 2px solid red;
      padding: 8px;
      margin: auto;
      overflow-x: hidden;
    }

    #scrollarea {
      height: 300px;
      width: 400px;
      border: 1px dashed green;
      padding: 16px;
    }

    input[type=range] {
      appearance: none;
      background: linear-gradient(90deg, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
      outline: none;
      opacity: 0.7;
      border-radius: 4px;
      height: 14px;
      width: 200px;
      box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
      vertical-align: middle;
      margin: 16px;
    }

    input[type=text] {
      width: 50px;
    }
  <div id="outerframe">
    <div id="scrollarea">
      <input type="range" id="rangescroll">
      <input type="text" value="50">
    </div>
  </div>

  <script>
    scrollarea.firstElementChild.addEventListener('input', evt => {
      evt.target.nextElementSibling.value = evt.target.value;
    })
  </script>

也许这不是最好的解决方案,但它似乎可行。仅使用 touchstarttouchend 事件 + css overflow:

scrollarea.firstElementChild.addEventListener("input", (evt) => {
  evt.target.nextElementSibling.value = evt.target.value;
});
scrollarea.firstElementChild.addEventListener("touchstart", (evt) => {
  outerframe.classList.add("overflow-hidden");
});
scrollarea.firstElementChild.addEventListener("touchend", (evt) => {
  outerframe.classList.remove("overflow-hidden");
});
#outerframe {
  height: 310px;
  width: 300px;
  border: 2px solid red;
  padding: 8px;
  margin: auto;
  overflow: scroll;
}

#scrollarea {
  height: 300px;
  width: 400px;
  border: 1px dashed green;
  padding: 16px;
}

input[type=range] {
  appearance: none;
  background: linear-gradient(90deg, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
  outline: none;
  opacity: 0.7;
  border-radius: 4px;
  height: 14px;
  width: 200px;
  box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
  vertical-align: middle;
  margin: 16px;
}

input[type=text] {
  width: 50px;
}

.overflow-hidden {
  overflow: hidden !important;  
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
  <meta charset="UTF-8">
  <title>Testing range in scroll</title>
</head>

<body>
  <div id="outerframe">
    <div id="scrollarea">
      <input type="range" id="rangescroll">
      <input type="text" value="50">
    </div>
  </div>


</body>

</html>

您可以将 css 属性 touch-action:manipulation; 添加到您的 #outerframe 以在智能手机模式下禁用来自它的触摸事件

编辑:我发现 touch-action:manipulation; 可能无法解决问题,但 touch-action:none 可以,但代价是渲染 overflow:scroll; 在移动设备中无用

第二次编辑:我发现添加 touch-action: pan-x; touch-action: pan-y 而不是 touch-action:none; 仍然不能使您的外部 div 以某种方式滚动

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1" />
  <meta charset="UTF-8">
  <style>
    #outerframe {
      height: 310px;
      width: 300px;
      border: 2px solid red;
      padding: 8px;
      margin: auto;
      overflow: scroll;
    touch-action: pan-x;
    touch-action: pan-y;
    }

    #scrollarea {
      height: 300px;
      width: 400px;
      border: 1px dashed green;
      padding: 16px;
    touch-action: manipulation;
    }

    input[type=range] {
      appearance: none;
      background: linear-gradient(90deg, #aaa, #ccf1ff 30%, #ccf1ff 70%, #aaa);
      outline: none;
      opacity: 0.7;
      border-radius: 4px;
      height: 14px;
      width: 200px;
      box-shadow: 0 0 8px rgba(0, 0, 0, 0.5) inset, 0 -5px 4px rgba(0, 0, 0, 0.5) inset;
      vertical-align: middle;
      margin: 16px;
    }

    input[type=text] {
      width: 50px;
    }
  </style>
  <title>Testing range in scroll</title>
</head>
<body>
  <div id="outerframe">
    <div id="scrollarea">
      <input type="range" id="rangescroll">
      <input type="text" value="50">
    </div>
  </div>

  <script>
    scrollarea.firstElementChild.addEventListener('input', evt => {
      evt.target.nextElementSibling.value = evt.target.value;
    })
  </script>
</body>
</html>