您可能已经看到过许多关于如何设置 range 输入样式的教程。虽然这篇文章也是关于这个主题的,但它并不是关于如何获得任何特定的视觉效果。相反,它深入探讨了浏览器的不一致性,详细说明了每个浏览器如何显示屏幕上的滑块。理解这一点很重要,因为它有助于我们清楚地了解我们是否可以使我们的滑块在所有浏览器中看起来和行为一致,以及哪些样式是必要的。
查看 Range 输入内部
在任何其他操作之前,我们需要确保浏览器公开了 Range 输入**内部**的 DOM。
在 Chrome 中,我们打开 DevTools,转到**设置**,**首选项**,**元素**,并确保启用**显示用户代理 Shadow DOM**选项。

在 Firefox 中,我们转到 about:config
并确保 devtools.inspector.showAllAnonymousContent
标志设置为 true
。

很长一段时间,我一直相信 Edge 无法查看这些元素内部的内容。但在使用它的过程中,我发现只要有意志(以及一些运气),总有办法!我们需要打开 DevTools,然后转到要检查的 range input
,右键单击它,选择**检查元素**,然后 bam,DOM 浏览器面板现在显示了我们滑块的结构!

显然,这是一个bug。但它也极其有用,所以我并不抱怨。
内部结构
从一开始,我们就可以看到潜在问题的根源:每个浏览器内部都有非常不同的东西。
在 Chrome 中,在 Shadow DOM 的顶部,我们有一个我们无法再访问的 div
。这在 /deep/
支持的时候是可能的,但随后穿透 Shadow Barrier 的能力被认为是一个 bug,因此曾经有用的功能被删除了。在这个 div
内部,我们还有另一个用于轨道的 div
,在轨道 div
内,我们还有第三个 div
用于滑块。最后两个都用 id
属性明确标记,但另一件我发现奇怪的事情是,虽然我们可以用 ::-webkit-slider-runnable-track
访问轨道,用 ::-webkit-slider-thumb
访问滑块,但只有轨道 div
有一个具有此值的 pseudo
属性。

在 Firefox 中,我们也看到内部有三个 div
元素,但这次它们不是嵌套的——所有三个都是兄弟元素。此外,它们只是普通的 div
元素,没有用任何属性标记,因此在第一次查看它们时,我们无法分辨哪个是哪个组件。幸运的是,在检查器中选择它们会在页面上突出显示相应的组件,这就是我们如何知道第一个是轨道,第二个是进度,第三个是滑块的方式。

我们可以用 ::-moz-range-track
访问轨道(第一个 div
),用 ::-moz-range-progress
访问进度(第二个 div
),用 ::-moz-range-thumb
访问滑块(最后一个 div
)。
Edge 中的结构要复杂得多,这在某种程度上允许对滑块样式进行更大程度的控制。但是,我们只能使用以 -ms-
为前缀的 ID 访问元素,这意味着我们也无法访问许多元素,这些元素具有我们通常需要更改的内置样式,例如实际 input
和其轨道之间的元素上的 overflow: hidden
或滑块父元素上的 transition
。

拥有不同的结构并且无法访问内部的所有元素以按我们希望的方式设置所有样式,意味着即使必须为每个浏览器使用不同的伪元素来帮助设置单个样式,在所有浏览器中获得相同的结果也可能非常困难,甚至是不可能的。
我们应该始终力求将单个样式保持在最低限度,但有时这根本不可能,因为设置相同的样式可能会由于具有不同的结构而产生非常不同的结果。例如,在轨道上设置诸如 opacity
或 filter
甚至 transform
之类的属性也会影响 Chrome 和 Edge 中的滑块(其中它是轨道的子元素/后代),但在 Firefox 中不会(其中它是其兄弟元素)。
我发现设置通用样式的最有效方法是使用 Sass 混合,因为以下方法不起作用
input::-webkit-slider-runnable-track,
input::-moz-range-track,
input::-ms-track { /* common styles */ }
为了使其工作,我们需要这样编写
input::-webkit-slider-runnable-track { /* common styles */ }
input::-moz-range-track { /* common styles */ }
input::-ms-track { /* common styles */ }
但这重复太多,而且维护起来很麻烦。这就是使混合解决方案成为最合理的选择的原因:我们只需要编写一次通用样式,因此,如果我们决定修改通用样式中的某些内容,则只需要在一个地方进行更改——在混合中。
@mixin track() { /* common styles */ }
input {
&::-webkit-slider-runnable-track { @include track }
&::-moz-range-track { @include track }
&::-ms-track { @include track }
}
请注意,我在这里使用的是 Sass,但您可以使用任何其他预处理器。只要它避免重复并使代码更易于维护,您喜欢什么都可以。
初始样式
接下来,我们来看一下滑块及其组件附带的一些默认样式,以便更好地理解需要显式设置哪些属性以避免浏览器之间的视觉不一致。
事先警告:事情很混乱且复杂。不仅不同浏览器有不同的默认值,而且更改一个元素上的属性可能会以意想不到的方式更改另一个元素(例如,当设置 background
也更改 color
并添加 border
时)。
WebKit 浏览器和 Edge(因为,是的,Edge 也应用了许多 WebKit 前缀的内容)也对某些属性(例如与尺寸、边框和背景相关的属性)有两个级别的默认值,如果我们可以这样称呼它们——在设置 -webkit-appearance: none
之前(如果没有它,我们在这些浏览器中设置的样式将不起作用)和设置之后。重点将放在设置 -webkit-appearance: none
后的默认值上,因为在 WebKit 浏览器中,我们无法在不设置此属性的情况下设置 range 输入的样式,并且我们进行所有这些操作的全部原因是为了理解如何在设置滑块样式时使我们的生活更轻松。
请注意,在 range input
和滑块上设置 -webkit-appearance: none
(由于某种原因,轨道默认情况下已设置)会导致滑块在 Chrome 和 Edge 中完全消失。为什么会发生这种情况,我们将在本文后面稍作讨论。
实际的 Range 输入元素
我考虑检查的第一个属性 box-sizing
碰巧在所有浏览器中都具有相同的值——content-box
。我们可以通过在 DevTools 的**计算**选项卡中查找 box-sizing
属性来查看这一点。

input
的 box-sizing
,所有三个浏览器的比较视图(从上到下:Chrome、Firefox、Edge)。可悲的是,这并不能说明即将发生的事情。一旦我们查看提供元素框的属性(margin
、border
、padding
、width
、height
),这一点就会变得很明显。
默认情况下,Chrome 和 Edge 中的 margin
为 2px
,Firefox 中为 0 .7em
。
在我们继续之前,让我们看看我们是如何获得上述值的。我们得到的计算长度值始终是 px
值。
但是,Chrome 向我们展示了浏览器样式是如何设置的(用户代理样式表规则在灰色背景上设置)。有时我们获得的计算值不是显式设置的,因此这没有用,但在这种特殊情况下,我们可以看到 margin
确实设置为 px
值。

margin
案例。Firefox 在某些情况下也允许我们跟踪浏览器样式的来源,如下面的屏幕截图所示

input
的 margin
如何失败。但是,在这种特殊情况下,这不起作用,因此我们可以做的是查看 DevTools 中的计算值,然后检查这些计算值是否在以下情况之一中发生变化
- 更改
input
或html
上的font-size
时,这意味着它设置为em
或rem
值。 - 更改视口时,这表示该值使用
%
值或视口单位设置。不过,在很多情况下,这可能可以安全地跳过。

input
的 font-size
也会更改其 margin
值。Edge 也是如此,我们可以在其中跟踪用户样式的来源,但不能跟踪浏览器样式,因此我们需要检查计算出的 px
值是否依赖于其他任何内容。

input
的 font-size
不会更改其 margin
值。
无论如何,这意味着如果我们希望在所有浏览器中实现一致的外观,我们需要在input[type='range']
中显式设置margin
属性。
既然我们提到了font-size
,让我们也检查一下它。当然,这也是不一致的。
首先,Chrome 中的计算值为13.3333px
,尽管小数可能暗示它是计算结果(我们将一个数字除以 3 的倍数),但它似乎被设置为这样,并且不依赖于视口尺寸或父级或根font-size
。

input
的font-size
。Firefox 显示了相同的计算值,但它似乎来自将font
简写设置为-moz-field
,我一开始对此感到非常困惑,尤其是在background-color
设置为-moz-Field
的情况下,因为 CSS 关键字不区分大小写,它们应该相同。但是如果它们相同,那么它怎么能成为这两个属性的有效值呢?显然,此关键字是某种使输入看起来像当前操作系统上的任何输入的别名。

input
的font-size
。最后,Edge 为其计算值提供了16px
,这似乎是从其父级继承而来或设置为1em
,如下面的记录所示。

input
的font-size
。这很重要,因为我们通常希望使用em
单位来设置滑块和控件(及其组件)的尺寸,以便它们相对于页面上文本的大小保持不变——当我们增加文本大小时它们看起来不会太小,当我们减小文本大小时它们看起来也不会太大。如果我们要以em
单位设置尺寸,那么浏览器之间存在明显的font-size
差异会导致我们的范围input
在某些浏览器中变小,在其他浏览器中变大。
出于这个原因,我总是确保在实际的滑块上显式设置font-size
。或者我可能会设置font
简写,即使其他与字体相关的属性在此处此时并不重要。也许将来会重要,但稍后我们讨论刻度标记和刻度标记标签时会详细介绍。
在我们继续讨论边框之前,让我们先看看color
属性。在 Chrome 中,它是rgb(196,196,196)
(如此设置),这使得它比silver
(rgb(192,192,192)
/ #c0c0c0
)略亮,而在 Edge 和 Firefox 中,计算值为rgb(0,0,0)
(纯黑色
)。我们无法知道 Edge 中此值是如何设置的,但在 Firefox 中,它是通过另一个类似的关键字-moz-fieldtext
设置的。

input
的color
,三个浏览器的对比外观(从上到下:Chrome、Firefox、Edge)。border
在 Chrome 中设置为initial
,等效于none medium currentcolor
(border-style
、border-width
和border-color
的值)。medium
边框的精确厚度取决于浏览器,但它至少与所有地方的thin
边框一样厚。尤其是在 Chrome 中,我们在这里得到的计算值为0
。

input
的border
。在 Firefox 中,我们也为border
设置了none medium currentcolor
值,尽管这里的medium
似乎等效于0.566667px
,这是一个不依赖于元素或根font-size
或视口尺寸的值。

input
的border
。我们无法看到 Edge 中所有内容是如何设置的,但border-style
和border-width
的计算值分别为none
和0
。当我们更改color
属性时,border-color
会发生变化,这意味着,就像在其他浏览器中一样,它被设置为currentcolor
。

input
的border
。padding
在 Chrome 和 Edge 中均为0
。

input
的padding
,Chrome(顶部)和 Edge(底部)的对比外观。但是,如果我们想要像素完美的渲染结果,则需要显式设置它,因为在 Firefox 中它被设置为1px
。

input
的padding
。现在让我们绕个弯,在尝试理解尺寸值的含义之前检查一下背景。在这里,我们发现 Edge 和 Firefox 中的计算值为transparent
/ rgba(0, 0, 0, 0)
,但在 Chrome 中为rgb(255,255,255)
(纯白色
)。

input
的background-color
,三个浏览器的对比外观(从上到下:Chrome、Firefox、Edge)。而且……最后,让我们看看尺寸。我把它留到最后,因为这里的事情开始变得非常混乱。
Chrome 和 Edge 都为width
的计算值提供了129px
。与之前的属性不同,我们无法在 Chrome 中看到任何地方设置了它,这通常会让我认为它依赖于父级,像所有block
元素一样水平拉伸以适应(这里肯定不是这种情况)或依赖于子元素。在“计算”面板中,还有一个-webkit-logical-width
属性取相同的值129px
。我一开始对此有点困惑,但事实证明它是书写模式相关的等效项——换句话说,它是水平书写模式的width
,垂直书写模式的height
。

input
的font-size
不会更改其width
值。无论如何,它在任一浏览器中都不依赖于input
本身或根元素的font-size
,也不依赖于视口尺寸。

input
的font-size
不会更改其width
值。Firefox 这里有点不同,为默认width
返回160px
的计算值。但是,此计算值确实依赖于范围input
的font-size
——它似乎是12em
。

input
的font-size
也会更改其width
值。对于height
,Chrome 和 Edge 再次达成一致,为我们提供了21px
的计算值。就像width
一样,我无法在 Chrome DevTools 中的 user agent 样式表中看到任何地方设置了它,这通常发生在元素的高度取决于其内容时。

input
的font-size
不会更改其height
值。此值在任一浏览器中也不依赖于font-size
。

input
的font-size
不会更改其height
值。Firefox 再次有所不同,将17.3333px
作为计算值,并且,同样,这取决于input
的font-size
——它是1.3em
。

input
的font-size
也会更改其height
值。但这不比margin
情况更糟糕,对吧?好吧,到目前为止,还没有!但这正要发生变化,因为我们现在要继续讨论轨道组件了。
范围轨道组件
关于实际input
尺寸,我们还有一个尚未考虑的可能性:它们受其组件的影响。因此,让我们在轨道上显式设置一些尺寸,看看是否会影响滑块的大小。
显然,在这种情况下,对于实际的滑块,width
没有任何变化,但在轨道width
方面,我们可以发现更多不一致之处,默认情况下,在所有三个浏览器中,轨道都会拉伸以填充父级input
的content-box
。
在 Firefox 中,如果我们显式设置width
,轨道上的任何width
,则轨道将采用我们提供的此width
,扩展到其父滑块外部或缩小到内部,但始终与其垂直居中对齐。这还不错,但遗憾的是,事实证明 Firefox 是唯一在此处以正常方式运行的浏览器。

width
会更改轨道的width
,但不会更改父滑块的width
。在 Chrome 中,我们设置的轨道width
被完全忽略,并且看起来没有合理的方法使它具有不依赖于父滑块的值。

width
在 Chrome 中没有任何作用(计算值保持129px
)。至于不合理的方法,使用transform: scaleX(factor)
似乎是使轨道比其父滑块更宽或更窄的唯一方法。请注意,这样做也会导致一些副作用。拇指也会水平缩放,并且其运动仅限于 Chrome 和 Edge 中缩小的轨道(因为拇指是这些浏览器中轨道的子元素),但在 Firefox 中则不然,其中其大小保持不变,并且其运动仍然仅限于输入,而不是缩小的轨道(因为轨道和拇指在这里是同级元素)。轨道上的任何横向padding
、border
或margin
也会被缩放。
继续讨论 Edge,轨道再次采用我们设置的任何width
。

width
。但这与 Firefox 的情况不同。虽然在轨道上设置大于父滑块的width
会使其向外扩展,但两者没有垂直居中对齐。相反,轨道的左边界与其实际范围input
父级的左内容边界左对齐。这种对齐不一致本身并不是什么大问题——仅在::-ms-track
上设置margin-left
就可以解决它。
但是,Edge 中父滑块content-box
外部的所有内容都会被剪裁掉。这等效于在实际的input
上将overflow
设置为hidden
,这将剪裁掉padding-box
外部的所有内容,而不是content-box
。因此,无法通过在滑块上设置overflow: visible
来修复它。
此剪裁是由input
和轨道之间的元素具有overflow: hidden
引起的,但是,由于我们无法访问这些元素,因此我们也无法解决此问题。在某些情况下,将所有内容设置为没有任何组件(包括其box-shadow
)超出范围的content-box
是一个选项,但并非总是如此。
对于height
,Firefox 的行为与其对width
的行为类似。轨道垂直扩展或缩小到我们设置的height
,而不会影响父滑块,并且始终与其垂直垂直居中对齐。

height
会更改轨道的height
,但不会更改父滑块的height
。在实际input
或轨道上没有设置任何样式的情况下,此height
的默认值为.2em
。

font-size
会更改 Firefox 中其计算的height
。
与width
的情况不同,Chrome 允许滑块轨道采用我们设置的height
值,并且如果我们在这里没有使用%
值,它还会使父滑块的content-box
扩展或收缩,从而使轨道的border-box
完美地嵌套其中。当使用%
值时,实际的滑块和轨道会在垂直方向上居中对齐。

%
单位的height
会更改 Chrome 中轨道的height
,但不会更改父滑块的height
。使用其他单位时,实际的范围input
会在垂直方向上扩展或收缩,以使轨道完美地嵌套其中。在没有设置任何自定义样式的情况下,我们获得的height
的计算值与滑块相同,并且不会随font-size
更改。

font-size
不会更改 Chrome 中其计算出的height
。Edge 怎么样?好吧,我们可以独立于父滑块更改轨道的height
,并且两者都保持垂直居中对齐,但这仅在我们设置的轨道height
小于实际input
的初始height
时才有效。高于此值时,轨道的计算height
始终等于父范围的height
。

height
不会更改父滑块的height
,并且两者居中对齐。但是,轨道的height
受实际input
的height
限制。初始轨道高度为11px
,此值不依赖于font-size
或视口。

font-size
不会更改 Edge 中其计算出的height
。接下来讨论一些不那么令人费解的内容,即box-sizing
。Chrome 中为border-box
,Edge 和 Firefox 中为content-box
,因此,如果我们要设置非零的border
或padding
,则需要显式设置box-sizing
属性以使各方面保持一致。

box-sizing
,三个浏览器(从上到下:Chrome、Firefox、Edge)的对比。在所有三个浏览器中,轨道的默认margin
和padding
均为0
——终于出现了一致性!

box-sizing
,三个浏览器(从上到下:Chrome、Firefox、Edge)的对比。在所有三个浏览器中,color
属性的值可以从父滑块继承。

color
,Chrome(顶部)和 Firefox(底部)的对比。即便如此,Edge 仍然是这里特立独行的存在,将其更改为white
,尽管将其设置为initial
会将其更改为black
,这是实际input
的值。

color
重置为initial
。在 Edge 中,在实际的input
上设置-webkit-appearance: none
会使轨道上color
的计算值变为transparent
(如果我们没有自己显式设置color
值)。此外,一旦我们在轨道上添加了background
,计算出的轨道color
就会突然变为black
。

background
轨道后产生的意外后果。在某种程度上,能够继承color
属性对于主题设置很有用,尽管继承自定义属性在这里可以做更多的事情。例如,假设我们想使用银色表示次要内容,使用橙色表示我们想要突出显示的内容。我们可以在body
上定义两个 CSS 变量,然后在页面上使用它们,甚至在我们的范围输入中使用它们。
body {
--fading: #bbb;
--impact: #f90
}
h2 { border-bottom: solid .125em var(--impact) }
h6 { color: var(--fading) }
[type='range']:focus { box-shadow: 0 0 2px var(--impact) }
@mixin track() { background: var(--fading) }
@mixin thumb() { background: var(--impact) }
遗憾的是,虽然这在 Chrome 和 Firefox 中有效,但 Edge 目前不允许将范围input
上的自定义属性继承到其组件中。

默认情况下,Chrome 或 Firefox 中的轨道上没有border
(border-width
为0
,border-style
为none
)。

border
,Chrome(顶部)和 Firefox(底部)的对比。如果我们在实际的输入上没有设置background
,并且在轨道本身也没有设置background
,则 Edge 中的轨道上没有border
。但是,一旦更改,我们就会得到一个细的(1px
)黑色轨道border
。

background
后的另一个意外后果。默认的background-color
显示为继承的白色,但随后在 Chrome 中,我们以某种方式获得了rgba(0,0,0,0)
(transparent
)的计算值(在-webkit-appearance: none
之前和之后)。这也让我好奇,我们之前是如何看到轨道的,因为没有background-color
或background-image
提供任何可见内容。Firefox 给我们rgb(153,153,153)
(#999
)的计算值,而 Edge 给我们transparent
(即使我们最初可能认为它是一种银色,但这并不是::-ms-track
元素的background
——稍后会详细介绍)。

background-color
,三个浏览器(从上到下:Chrome、Firefox、Edge)的对比。范围滑块组件
准备好迎接迄今为止最恼人的不一致了吗?在 Chrome 中,滑块移动 限制在轨道的content-box
内,而在 Firefox 和 Edge 中,即使我们使轨道比输入长或短,滑块也限制在实际input
的content-box
内(Chrome 不允许这样做,它强制轨道的border-box
在水平方向上适合滑块的content-box
)。
Chrome 的行为如下所示

填充是透明的,而内容框和边框是半透明的。我们使用橙色表示实际的滑块,红色表示轨道,紫色表示滑块。
对于 Firefox,情况略有不同

border-box
在水平方向上完美地适合滑块的content-box
,它更长,它更短)。在 Chrome 中,滑块是轨道的子元素,而在 Firefox 中,它是其同级元素,因此,从这个角度来看,Chrome 将滑块移动到轨道content-box
的限制范围内是有道理的,而 Firefox 将其移动到滑块content-box
的限制范围内是有道理的。但是,滑块也在 Edge 中位于轨道内,并且它仍然在滑块的content-box
的限制范围内移动。

border-box
在水平方向上完美地适合滑块的content-box
,它更长,它更短)。虽然这乍一看非常奇怪,但这是因为 Edge 强制将轨道的position
设置为static
,即使我们使用!important
将其设置为relative
,我们也无法更改它。

position
属性的值。这意味着我们可以在所有浏览器中以完全相同的方式设置滑块的样式,但是如果其content-box
在水平方向上与轨道的content-box
不重合(因此,如果我们在轨道上设置了非零的横向padding
或border
),则它在所有浏览器中的移动范围将不相同。
此外,如果我们水平缩放轨道,则 Chrome 和 Firefox 的行为与之前相同,滑块在 Chrome 中移动到现在已缩放的轨道的content-box
的限制范围内,在 Firefox 中移动到实际input
的content-box
的限制范围内。但是,Edge 使滑块在一个宽度等于轨道border-box
的间隔内移动,但从轨道padding-box
的左边界开始,这可能是由transform
属性创建了一个堆叠上下文造成的。

在垂直方向上,Firefox 中的滑块与轨道垂直居中,Edge 中似乎也是垂直居中,尽管我在多次测试相同情况时得到了非常混乱的不同结果,并且一旦我们在实际的input
和滑块上设置了-webkit-appearance: none
以便我们可以设置滑块的样式,其border-box
的顶部就会与轨道的content-box
的顶部对齐。
虽然 Chrome 的决定乍一看很奇怪,在大多数情况下都很烦人,并且最近甚至导致了 Edge 中出现问题(但稍后会详细介绍),但它背后确实有一些逻辑。默认情况下,Chrome 中轨道的height
由滑块的height
决定,如果我们从这个角度来看,顶部对齐似乎不再完全是疯狂的了。
但是,我们通常希望滑块比轨道的height
更大,并且与轨道垂直居中。我们可以使用我们在::-webkit-slider-thumb
伪元素上设置的样式中的margin-top
来更正 Chrome 的对齐方式。
不幸的是,这样我们就会破坏 Edge 中的垂直对齐方式。这是因为 Edge 现在也应用了通过::-webkit-slider-thumb
设置的样式。至少我们可以选择在我们在::-ms-thumb
上设置的样式中将margin-top
重置为0
。下面的演示展示了一个非常简单的示例。
查看 thebabydino 在 CodePen 上创建的 Pen(@thebabydino)。
就像轨道的情况一样,box-sizing
属性的值在 Chrome 中为border-box
,在 Edge 和 Firefox 中为content-box
,因此,为了在浏览器之间获得一致的结果,如果我们想要在滑块上设置非零的border
或padding
,则需要显式设置它。
在所有三个浏览器中,margin
和padding
默认都为0
。
在滑块和滑块上都设置了-webkit-appearance: none
之后(只在一个上设置不会改变任何内容),滑块的尺寸从10x21
(不依赖于font-size
的尺寸)重置为 Chrome 中的129x0
。轨道和实际滑块的height
也会重置为0
,因为它们取决于其内容(内部的滑块,其height
已变为0
)。

这也是为什么在滑块上显式设置height
会导致轨道采用相同height
的原因。
根据 Chrome DevTools,在这两种情况下都没有border
,即使在设置-webkit-appearance: none
之前,它看起来确实有一个border
。

-webkit-appearance: none
之前 Chrome 中滑块的外观。
如果这不是一个border
,它可能是一个outline
或一个没有模糊且具有正扩展的box-shadow
。但是,根据Chrome DevTools,我们在拇指上既没有outline
,也没有box-shadow
。

outline
和 box-shadow
的计算值。在 Edge 中设置-webkit-appearance: none
会使拇指尺寸从11x11
(不依赖于font-size
的值)变为0x0
。显式地为拇指设置height
会使轨道采用初始height
(11px
)。

在 Edge 中,最初拇指上没有border
。但是,在为实际的范围input
或其任何组件设置background
之后,我们突然得到一个solid 1px white
的横向边框(左侧和右侧,但不是顶部和底部),在:active
状态下视觉上变为black
(即使 Edge DevTools 似乎没有注意到这一点)。设置-webkit-appearance: none
会移除border-width
。

border
。在 Firefox 中,如果没有在范围input
或其组件上设置像background
这样的属性,则拇指的尺寸为1.666x3.333
,在这种情况下,它们不会随font-size
更改。但是,如果我们在滑块上设置类似background: transparent
的内容(或其组件上的任何background
值),则拇指的width
和height
都将变为1em
。

在 Firefox 中,如果我们相信我们在 DevTools 中看到的内容,我们最初有一个实心的粗灰色(rgb(153, 153, 153)
)border
。

border
。然而,在视觉上,我在任何地方都找不到这个粗灰色的border
。

在为实际的范围input
或其组件之一设置background
之后,拇指border
实际上变得可以视觉检测到,并且似乎是.1em
。

border
。在 Chrome 和 Edge 中,border-radius
始终为0
。

border-radius
。然而,在 Firefox 中,此属性的值为.5em
,无论是在为范围input
或其组件设置background
之前还是之后,即使拇指的初始形状看起来不像具有圆角的矩形。

border-radius
。Firefox 中拇指的奇怪初始形状让我怀疑它是否没有设置clip-path
,但根据 DevTools 并非如此。

clip-path
。更有可能的是,拇指形状是由于-moz-field
设置造成的,尽管至少在 Windows 10 上,这并不会使其看起来像其他所有滑块。

Chrome DevTools 报告拇指的background-color
为rgba(0, 0, 0, 0)
(transparent
),即使在设置-webkit-appearance: none
之前它看起来是灰色的。我们似乎也没有一个可以解释设置-webkit-appearance: none
之前拇指上的渐变或线条的background-image
。Firefox DevTools 报告它为rgb(240, 240, 240)
,即使在没有显式地为实际的范围input
或其任何组件设置background
的情况下,它看起来是蓝色的。

background-color
。在 Edge 中,background-color
在设置-webkit-appearance: none
之前为rgb(33, 33, 33)
,之后为transparent
。

background-color
。范围进度(填充)组件
我们只在 Firefox(::-moz-range-progress
)和 Edge(::-ms-fill-lower
)中为此提供了专用的伪元素。请注意,此元素在 Firefox 中是轨道的同级元素,在 Edge 中是其后代。这意味着它在 Firefox 中相对于实际的input
进行大小调整,但在 Edge 中相对于轨道进行大小调整。
为了更好地理解这一点,请考虑轨道的border-box
在滑块的content-box
内完全水平地适应,并且轨道同时具有border
和padding
。
在 Firefox 中,进度组件的border-box
的左限制始终与滑块的content-box
的左限制一致。当当前滑块值为其最小值时,我们进度的border-box
的右限制也与滑块的content-box
的左限制一致。当当前滑块值为其最大值时,我们进度的border-box
的右限制与滑块的content-box
的右限制一致。
这意味着我们进度的border-box
的width
从0
变为滑块的content-box
的width
。一般来说,当拇指位于两个极限值之间距离的x%
处时,我们进度的border-box
的width
为滑块的content-box
的x%
。
这在下面的录制中显示。padding
区域始终是透明的,而border
区域和content-box
是半透明的(实际的input
为橙色,轨道为红色,进度为灰色,拇指为紫色)。

::-moz-range-progress
组件的width
如何变化。然而,在 Edge 中,填充的border-box
的左限制始终与轨道的content-box
的左限制一致,而填充的border-box
的右限制始终与垂直分割拇指的border-box
成两半的垂直线一致。这意味着当当前滑块值为其最小值时,填充的border-box
的右限制是拇指的border-box
的一半,位于轨道content-box
的左限制右侧。当当前滑块值为其最大值时,填充的border-box
的右限制是拇指的border-box
的一半,位于轨道content-box
的右限制左侧。
这意味着我们进度的border-box
的width
从拇指的border-box
的一半减去轨道的左侧border
和padding
变为轨道content-box
的width
加上轨道的右侧padding
和border
减去拇指的border-box
的一半。一般来说,当拇指位于两个极限值之间距离的x%
处时,我们进度的border-box
的width
为其最小width
加上其最大和最小width
之间差值的x%
。
以下录制对此进行了说明,您可以使用此现场演示进行操作。

::-ms-fill-lower
组件的width
如何变化。虽然上面对 Edge 方法的描述可能使其看起来更复杂,但我得出的结论是,这是改变此组件宽度的最佳方法,因为 Firefox 方法可能会导致一些问题。
例如,考虑我们为了跨浏览器一致性而在轨道上没有border
或padding
,并且填充和拇指的border-box
的height
都等于轨道的height
的情况。此外,拇指是一个圆盘(border-radius: 50%
)。
在 Edge 中,一切正常。

但在 Firefox 中,情况看起来很尴尬(现场演示)。

好消息是,在这种组件的情况下,我们没有其他令人讨厌且难以解决的不一致性。
box-sizing
在两个浏览器中具有相同的计算值 - content-box
。

box-sizing
的计算值:Firefox(顶部)和 Edge(底部)。在 Firefox 中,进度的height
为.2em
,而padding
、border
和margin
均为0
。

height
。在 Edge 中,填充的height
等于轨道的content-box
的height
,padding
、border
和margin
均为0
,就像在 Firefox 中一样。

height
。最初,此元素的background
在 Firefox 中为rgba(0, 0, 0, 0)
(transparent
,这就是为什么我们一开始看不到它的原因)而在 Edge 中为rgb(0, 120, 115)
。

background-color
:Firefox(顶部)和 Edge(底部)。在这两种情况下,color
属性的计算值均为rgb(0, 0, 0)
(纯black
)。

color
的计算值:Firefox(顶部)和 Edge(底部)。WebKit 浏览器不提供此类组件,并且由于我们不再有访问和使用轨道::before
或::after
伪元素的方法,因此我们模拟此组件的唯一选择仍然是在这些浏览器中在轨道的现有背景之上叠加一个额外的、非重复的background
,并使此额外图层沿x
轴的大小取决于范围input
的当前值。
如今执行此操作的最简单方法是使用当前值--val
CSS 变量,该变量保存滑块的当前值。每次滑块的值发生变化时,我们都会更新此变量,并将此顶层图层的background-size
设为取决于--val
的calc()
值。这样,当范围input
的值发生变化时,我们就不必重新计算任何内容 - 我们的calc()
值是动态的,因此更新--val
变量就足够了(不仅适用于此background-size
,还适用于可能依赖于它的其他样式)。
请参阅 thebabydino 在 CodePen 上的 Pen (@thebabydino)。
如果::-moz-range-progress
的增长方式对我们的特定用例看起来不好,也可以为 Firefox 执行此操作。
Edge 还提供了一个::-ms-fill-upper
,它基本上是下部组件的补充,并且是我们最初在拇指右侧看到的银色background
,而不是轨道的background
(轨道是transparent
)。
刻度和标签
Edge 是唯一默认显示刻度的浏览器。它们显示在轨道上,分隔两段、五段、十段、二十段,确切数量最初取决于轨道width
。我们可以更改这些刻度的唯一样式是color
属性,因为它继承自轨道(因此在轨道上设置color: transparent
会移除 Edge 中的初始刻度)。

该规范说明可以通过链接datalist
元素添加刻度和标签,对于其option
子元素,如果我们希望该特定刻度也具有标签,则可以为其指定label
属性。
不幸的是,尽管在这一点上并不奇怪,但浏览器在这里也有自己的想法。Firefox 没有任何显示 - 没有刻度,没有标签。Chrome 显示刻度,但仅允许我们使用option
值控制它们在滑块上的位置。它不允许我们以任何方式设置它们的样式,也不显示任何标签。

此外,在实际滑块上设置-webkit-appearance: none
(这是为了能够设置其样式而需要执行的操作)会导致这些刻度消失。
Edge 也加入了这个行列,也不显示任何标签,并且对刻度的外观控制也不多。虽然添加datalist
允许我们控制在轨道上显示哪些刻度,但我们无法对其进行样式设置,除了更改轨道组件上的color
属性。

在 Edge 中,我们还有::-ms-ticks-before
和::-ms-ticks-after
伪元素。它们基本上就是它们听起来的样子 - 轨道之前和之后的刻度。但是,我很难理解它们是如何工作的。
它们被display: none
隐藏,因此将此属性更改为block
会使它们可见,如果我们还显式设置滑块height
,即使这样做不会更改它们自己的height
。

::-ms-ticks-after
创建的刻度可见。除此之外,我们可以设置诸如margin
、padding
、height
、background
、color
之类的属性来控制它们的外观。但是,我不知道如何控制单个刻度的粗细,如何为单个刻度提供渐变背景,或者如何使其中一些成为主刻度,一些成为次刻度。
因此,归根结底,如果我们想要一个不错的跨浏览器结果,我们最好的选择仍然是使用repeating-linear-gradient
作为刻度,以及label
元素作为这些刻度对应的值。
请参阅thebabydino (@thebabydino)在CodePen上的Pen。
工具提示/当前值显示
Edge 是唯一通过::-ms-tooltip
提供工具提示的浏览器,但这不会显示在 DOM 中,无法真正设置样式(我们只能选择通过在其上设置display: none
来隐藏它),并且只能显示整数,因此对于范围在例如.1
和.4
之间的范围input
完全没用 - 它显示的所有值都是0
!

::-ms-tooltip
。因此,我们最好的选择是隐藏它,并对所有浏览器使用output
元素,再次利用将当前滑块值存储到--val
变量中,然后使用根据此变量计算出的calc()
值作为位置的可能性。
请参阅thebabydino (@thebabydino)在CodePen上的Pen。
方向
好消息是每个浏览器都允许我们创建垂直滑块。坏消息是,你可能已经猜到了……每个浏览器都提供了不同的方法来做到这一点,其中没有任何一个是规范中提出的(在范围input
上设置width
小于height
)。WebKit 浏览器选择了-webkit-appearance: slider-vertical
,Edge 选择了writing-mode: bt-lr
,而 Firefox 通过orient
属性及其值为'vertical'
来控制这一点。
真正糟糕的消息是,对于 WebKit 浏览器,以这种方式使滑块垂直会让我们无法在其上设置任何自定义样式(因为设置自定义样式需要-webkit-appearance
的值为none
)。
我们最好的选择是将范围input
的样式设置为水平,然后使用 CSS transform
将其旋转。
请参阅thebabydino (@thebabydino)在CodePen上的Pen。
你可能可以通过在 Firefox 的地址栏中输入
resource://gre-resources/forms.css
来回答更多问题 - 那里 UA 样式表对input[type="range"]
有专门的注释。……包括诸如“特别是‘margin’、‘top’和‘left’属性会被忽略”之类的宝石。
哇!你在这里真的做了很多工作,弄清了所有变化,并在大多数常用浏览器中提出了一个可行的(即使不是那么优雅的)解决方案。对你辛勤的工作和详尽的撰写表示衷心的感谢。
跑题了,而且我在这里是新手,所以如果答案很明显,请原谅我:你用什么制作了那些很棒的图像注释?特别是那种漂亮的草书字体是什么?谢谢!
Paint 3D,我不想安装任何新东西,所以我尝试用 Windows 自带的东西来做。字体是 Segoe Script。
Ana,
谢谢你的帮助!
Rick
伟大的奥丁的乌鸦!这肯定需要一些工作才能组合在一起。谢谢!
从某种程度上来说,可能每个属性都需要设置(浏览器在设置或推断其自身值的地方)是件好事 - 例如,有一天我注意到,将鼠标悬停在我的 Firefox 中的默认(未设置样式的)按钮上会使它们变成黑色背景和黑色文本。
事实证明,这是因为我正在使用来自操作系统的“我视力不好”高对比度主题,按钮的背景通过切换来处理这种情况,但颜色是在其他地方设置的(因此没有与操作系统的文本信息发生反应)。我想通常情况下,它会像我的其他应用程序一样,将颜色变成白色。
当然,Chromium 不会监听我的操作系统设置,所以这是我在 Firefox 中注意到的一个问题。如果 Edge 与 IE 类似,它也应该监听操作系统的信息。
因此,对于想要设置表单控件等喜欢查看操作系统的开发人员来说,一个好的经验法则是尽可能手动设置所有内容。这样,至少你的预期对比度会保留下来。
不错,但与现实世界的滑块相比,这个圆形手柄中间有一个销钉,它向下进入滑块轨道。因此,手柄在逻辑上应该在滑块轨道之外的[handleWidth/2 – trackHeight]处停止。
无论如何,干得好!讨厌这些未开发的滑块插件。
哦,我的天,这真是太令人惊讶了!当我第一次开始使用范围输入时,我正是这样认为它们会表现的。我最终弄清楚了真正发生的事情,但一开始这一切都让人感到困惑……