范围输入的值气泡

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

HTML 中的范围输入是这样的

<input type="range" name="quantity" min="1" max="10">

支持它的浏览器中,它们看起来是这样的

这很棒。您可以将其用于任何需要从用户那里收集具有强制最小值和最大值的数字的情况。

但是您有没有注意到任何奇怪的地方?仅仅是范围输入本身并不会向用户传达他们实际将提交的数字。现在,如果您的输入类似于“您感觉如何?左边表示悲伤,右边表示快乐。” - 那么没关系,您可能不需要向用户显示数字。但我敢打赌,您需要显示数字的情况比不显示数字的情况更为常见。

公平地说,规范中提到

input 元素表示用于将元素的值设置为表示数字的字符串的控件,**但需要注意的是,确切的值并不重要**,允许 UA 提供比数字状态更简单的界面。

但是,拜托,仅仅因为我们想要一个很酷的滑块并不意味着我们应该阻止用户知道提交的值。我不会说浏览器应该更改其 UI 控件以显示该数字。我的意思是,我们应该自己构建它!

这是使用<output>标签的完美用例,该标签专门用于表单元素计算的值。以下是如何使用它的一个超简单实现

<input type="range" name="foo">
<output for="foo" onforminput="value = foo.valueAsNumber;"></output>

更新!使用原生 JavaScript 的新版本,效果也更好。

我们的目标是显示一个“气泡”,其中显示范围输入的当前值。

从输入值设置“气泡”的值只是提取范围值并将其放在气泡中的问题

range.addEventListener("input", () => {
  bubble.innerHTML = rangel.value;
});

技巧在于定位气泡沿着范围输入,使其与“拇指”一起滑动。为此,我们需要计算气泡需要向左移动多少%。因此,让我们创建一个函数来执行此操作,以使事情更简洁

range.addEventListener("input", () => {
  setBubble(range, bubble);
});

function setBubble(range, bubble) {
  const val = range.value;
  const min = range.min ? range.min : 0;
  const max = range.max ? range.max : 100;
  const newVal = Number(((val - min) * 100) / (max - min));
  bubble.innerHTML = val;

  // Sorta magic numbers based on size of the native UI thumb
  bubble.style.left = newVal = "%";
}

在这里,我们确保考虑了范围输入的 min 和 max 属性,并根据该范围内的当前值计算 0-100 之间的百分比位置。并非所有范围都是默认的 0-100 数字,因此假设范围在 0 到 200 的范围内值为 50,则为 25%。这考虑到了这一点。

但它有一个恼人的缺陷:气泡在开始时太靠左,在结束时太靠右。在范围输入上,拇指不会从左边缘悬挂,因此其中心位于开头,结尾处也是如此。就像滚动条一样,拇指的边缘停在轨道内。

我们可以在那里使用一些神奇的数字,这些数字似乎在不同浏览器之间都能很好地工作

// Sorta magic numbers based on size of the native UI thumb
  bubble.style.left = `calc(${newVal}% + (${8 - newVal * 0.15}px))`;

这是最终演示

我之所以受到启发去研究这个,是因为读者 Max Globa 编写了他们的版本,我将在这里发布

Max 版本的一个很酷的方面是范围输入是 CSS 样式的,因此拇指的确切大小是已知的。JavaScript 数学中有一些感觉有点神奇的数字,但至少它们基于 CSS 中关于拇指大小的真实数字。

其他版本

Dave Olsen 移植了(原始)想法,使其不依赖于 jQuery。这是该版本


Sean Stopnik


simurai


Vincent Durand


不要忘记 范围输入可以有 datalist,它们会在上面放置一些小刻度,这有点酷。


Ana Tudor 有 一个庞大的集合,其中许多通过其设计指示当前值。

😬 本文原始版本中的旧版本(jQuery,效果不佳)

出于历史原因,将其保留在此处。

让我们引入我们的朋友 jQuery 并开始我们的 CSS。目标如下。任何范围输入,任何时间,任何最小值/最大值/步长 - 我们都会在其上方显示一个气泡,显示当前值。

让我们首先设置 output 元素的样式。我们将将其绝对定位在输入上方。一旦我们通过 JavaScript 确定了它应该是什么,这将使我们能够调整左侧的值。我们将用渐变和圆角来装饰它,甚至使用伪元素添加一个小指针三角形。

output { 
  position: absolute;
  background-image: linear-gradient(top, #444444, #999999);
  width: 40px; 
  height: 30px; 
  text-align: center; 
  color: white; 
  border-radius: 10px; 
  display: inline-block; 
  font: bold 15px/30px Georgia;
  bottom: 175%;
  left: 0;
  margin-left: -1%;
}
output:after { 
  content: "";
  position: absolute;
  width: 0;
  height: 0;
  border-top: 10px solid #999999;
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  top: 100%;
  left: 50%;
  margin-left: -5px;
  margin-top: -1px;
}

现在我们需要做的是观察所有范围输入的值变化。我们的目标是使气泡的左侧位置与滑块同步移动。这并不是世界上最简单的事情,因为滑块可以是任何宽度,并且可以具有任何最小值或最大值。我们将不得不进行一些计算。以下是所有 jQuery JavaScript 代码,已添加注释

// DOM Ready
$(function() {
 var el, newPoint, newPlace, offset;
 
 // Select all range inputs, watch for change
 $("input[type='range']").change(function() {
 
   // Cache this for efficiency
   el = $(this);
   
   // Measure width of range input
   width = el.width();
   
   // Figure out placement percentage between left and right of input
   newPoint = (el.val() - el.attr("min")) / (el.attr("max") - el.attr("min"));
   
   // Janky value to get pointer to line up better
   offset = -1.3;
   
   // Prevent bubble from going beyond left or right (unsupported browsers)
   if (newPoint < 0) { newPlace = 0; }
   else if (newPoint > 1) { newPlace = width; }
   else { newPlace = width * newPoint + offset; offset -= newPoint; }
   
   // Move bubble
   el
     .next("output")
     .css({
       left: newPlace,
       marginLeft: offset + "%"
     })
     .text(el.val());
 })
 // Fake a change to position bubble at page load
 .trigger('change');
});

其中一个糟糕的部分是该1.3值。我试图将气泡三角形的尖端与滑块的中心对齐。这并不容易,因为滑块的中心永远不会完全位于左侧或右侧。该值并不完美,也并非完美实现,但总比没有好。

作为奖励,不支持范围输入的浏览器仍然可以获得气泡操作。

以上代码依赖于范围输入具有指定的minmax值。如果没有,它就会中断。我认为在不指定这些内容的情况下使用范围输入会很奇怪,尽管如果您不指定,它们似乎默认为 0 和 100。为了防范这种情况,您需要获取每个属性,对其进行测试,如果看起来不对,则进行修复。例如

var minValue, maxValue;
if (!el.attr("min")) { minValue = 0; } else { minValue = el.attr("min"); }

…然后在数学中使用minValue变量。并对最大值执行类似的操作。无论如何,这是实时演示